How do we clear the icon in SwiftUI Pickers when switching from Label content to Text? - swiftui

I'm curious, is a mix of Label and Text Views supported as the contents of a Picker? I'm seeing some visual glitches around the icon that suggests it is not.
In the example below, the picker initially appears correctly.
Selecting "First Item" works as expected.
But reverting to the original state, by selecting "None", results in a label that still retains a phantom icon from the previous selection.
Can anyone suggest a good workaround, if this is a bug? I tried using a label with an empty string for the systemImage (Label("None", systemImage: "").tag(0)). I also tried using a Label instead of the text, but without icon (Label("None", systemImage: "house").labelStyle(.titleOnly).tag(0)). In both cases the phantom icon was still there.
Here is the full code to illustrate the problem:
struct LabelAndTextInPicker: View {
#State private var selection = 0
var body: some View {
Form {
Picker("Choice", selection: $selection) {
Text("None").tag(0)
Label("First Item", systemImage: "1.circle").tag(1)
Label("Second Item", systemImage: "2.circle").tag(2)
}
.pickerStyle(.menu)
}
}
}

It does look like a bug. As a workaround try this, works for me:
Label {
Text("None")
} icon: {
Image(uiImage: UIImage())
}.tag(0)

Related

Bring SwiftUI ButtomSheet behind tab bar

I want to use the new iOS 16 Buttom Sheet Feature to create a view like the find my:
Unfortunately the TabView gets hidden behind the Sheet:
My Code:
TabView {
Text("View 1")
.sheet(isPresented: .constant(true)) {
Text("Sheet View Content")
.presentationDetents([.medium, .large])
}
.tabItem {
Label("Menu", systemImage: "list.dash")
}
Text("View 2")
.tabItem {
Label("Order", systemImage: "square.and.pencil")
}
}
How can I achieve this?
No Solution for the moment
Unfortunately, it is not currently possible to show a modal sheet with a tab bar visible using the current version of SwiftUI. This issue has been raised by other developers on the Apple developer forums and on other threads on StackOverflow, such as the following:
https://developer.apple.com/forums/thread/711702
Swift UI show modal sheet with tab bar visible
SwiftUI present sheet in a TabItem of TabView using presentationDetents
I am also searching for a solution to this problem.
I am currently working on a custom ViewModifier to address this issue, and if it is successful, I will make it publicly available on GitHub.

Swiftui: How to Close one Tab in a TabView?

I'm learning SwiftUI and having trouble with closing Each Tab View Element.
My App shows photos from user's album by TabView with pageViewStyle one by one.
And What I want to make is user can click save button in each view, and when button is clicked, save that photo and close only that view while other photos are still displayed. So unless all photos are saved or discarded, if user clicks save button, TabView should automatically move to another one.
However, I don't know how to close only one Tab Element. I've tried to use dismiss() and dynamically changing vm.images element. Latter one actually works, but it displays awkward movement and it also requires quite messy code. How could I solve this issue?
Here is my code.
TabView {
ForEach(vm.images, id: \.self) { image in
TestView(image: image)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
struct TestView: View {
#ObservedObject var vm: TestviewModel
...
var body: some View {
VStack(spacing: 10) {
Image(...)
Spacer()
Button {
...
} label: {
Text("Save")
}
}
You need actually to remove saved image from the viewModel container, and UI will be updated automatically
literally
Button {
vm.images.removeAll { $0.id == image.id } // << here !!
} label: {
Text("Save")
}
You need to use the selection initializer of TabView in order to control what it displays. So replace TabView with:
TabView(selection: $selection)
Than add a new property: #State var selection: YourIdType = someDefaultValue, and in the Button action you set selection to whatever you want to display.
Also add .tag(TheIdTheViewWillUse) remember that whatever Id you use must be the same as your selection variable. I recommend you use Int for the simple use.

Set navigation bar item style in SwiftUI

When adding a navigation bar item with UIKit, you set its style with UIBarButtonItem.style. This is important for a Done button, which is displayed with bold text.
SwitftUI's navigationBarItems(leading:trailing:) takes a View but no style. You could hack a style look-alike by using a bold button in the view, but it won't adjust to future OS style changes (e.g. a font weight other than bold).
How do you set the navigation bar item's style with SwiftUI?
iOS 14+
It is worth noting that using ToolbarItem(placement:) within a toolbar modifier will automatically apply emboldened text to buttons in the .confirmationAction placement position.
For example:
struct MyView: View {
var body: some View {
NavigationView {
Form {
// other elements
}
.navigationTitle("Edit Publication")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { }
}
ToolbarItem(placement: .confirmationAction) {
Button("Save") { }
}
}
}
}
As you can see from the illustration below, the Save button appears in bold.
If you want a button in the same place as the Save button below but not to be emphasised, you'd use the .primaryAction modifier.
Using the placement types that describe toolbar items' context – rather than using the deprecated navigationBarItems modifier, or the .navigationBarTrailing and .navigationBarLeading placement values – is the best way to make your SwiftUI views adapt to any changes in future versions of iOS.
They're also applicable across multiple platforms that don't necessarily have navigation bars, and other platforms may choose to render them differently. For example, using .confirmationAction on macOS creates a button with the app accentColor as a background.
I think we have to change how we think about SwiftUI as the concepts of "UIBarButtonItem.style" won't be directly applicable. SwiftUI tries to hide implementation details and wants concepts like changing the font-weight to "auto-magically work" depending on the context.
On Xcode 12.3, and iOS 14.3, seems that by default the button styles are bold (in the context of NavigationView):
.navigationBarItems(
leading:
Button(action: {}) {
Text("Cancel")
},
trailing:
Button(action: {}) {
Text("Save")
}
)
One way to change styling is by adding a button style:
.navigationBarItems(
leading:
Button(action: {}) {
Text("Cancel")
}.buttonStyle(PlainButtonStyle()),
trailing:
Button(action: {}) {
Text("Save")
}
)
But that did not achieve the desired effect. I had to change the font weight to have the "Cancel" be a regular style, and "Save" be bold...just like standard iOS:
.navigationBarItems(
leading:
Button(action: {}) {
Text("Cancel")
.fontWeight(Font.Weight.regular)
},
trailing:
Button(action: {}) {
Text("Save")
}
)
The nice thing about this is that you don't need to know about the concept of "UIBarButtonItem.style:" you just need to know about the concepts of what a Button is, and what Text is - which API should be familiar over-time as they are standard building blocks.
in SwiftUI instead of passing a style you append it to the View component. this will adjust to future OS style changes:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View{
NavigationView {
Text("blah")
.navigationBarItems(leading: Text("done button")
.fontWeight(.medium)
.bold()
.foregroundColor(Color.red))
}
}
}
PlaygroundPage.current.setLiveView(ContentView())

How to do Apple Music-like navigation in SwiftUI? Custom List and NavigationView has highlight not going away

This is my example that I am trying to get to work:
struct ContentView: View {
let links = ["Item 1", "Item 2", "Item 3", "Item 4"]
var body: some View {
NavigationView {
ScrollView {
Text("My Title")
List(links, id: \.self) {
link in
NavigationLink(destination: TestView()) {
Text(link)
.padding(.vertical, 4)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.frame(height: 178)
Text("Some more content here")
}
}
}
}
Note: TestView is just some view with the text hello world on it.
I am trying to copy Apple Music's style of navigation. I tried putting a Button in the NavigationLink but tapping it on the text wouldn't change views, and I couldn't find a way to reliably change the color of the row when tapped, at the same time. Also in some approach, I managed to make it work, but the way the colors animate is different, i.e. it fades from A to B, over ~100ms whereas what I'm trying to achieve is to animate between the states instantly (like in Apple Music).
My current approach is using a List, putting NavigationLinks inside it and then cutting off the whole view by giving it a height. This way I can put it alongside other content.
It's working fine for now, but whenever I click on an row and go back, the row is still highlighted, when it shouldn't. Is there a way to make it so that it deselects when going back to the screen somehow?
I think this bug is being caused by the List being inside a ScrollView, since when I removed ScrollView, the list worked properly, and there wasn't this highlight bug. But I need to be able to put my content with the list, and I don't intend to have a list take up the whole screen.
Is there any way to fix this bug with this approach? I'm also willing for other ways to achieve the same result without using List.
Trying to use ForEach instead ofList?
With a view for row (CustomRow) where you can pass link item and set custom dividing line, background etc ...
ForEach(links, id: \.self) { link in
NavigationLink(destination: TestView()) {
CustomRow(item: link)
}
}
.frame(height: 178)

generic param Label cannot be inferred in SwiftUI

enter image description hereI am new to SwiftUI. When I embed a toggle switch inside Vstack, it says "generic param Label cannot be inferred" in SwiftUI. May I know the cause for this error and how to fix it.Thank you
Looks like you might have missed specifying the label or have an error there. I have created the below code that has a toggle button inside VStack.
import SwiftUI
struct ContentView: View {
#State private var isOn: Bool = false
var body: some View {
VStack (alignment: .leading, spacing: 10){
Toggle(isOn: $isOn){
Text("Toggle me to set values")
}
Text("Toggle Value: \(isOn.description)")
Spacer()
}.padding()
}
}
Let me know if this helps.
Toggle Screenshot
I have embedded a text, Two toggles and stepper and a button in Vstack and here I have missed to provide action to that button, which resulted in the issue "generic param Label cannot be inferred".previous Code with issue. After adding the button action