SwiftUI NavigationSplitView link is disabled in compact mode - ios

When implementing my app, I want to embed a NavigationStack inside NavigationSplitView, as the documentation says. However, under compact mode like on iPhone, I encountered a bug.
To be specific, here's the example code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationSplitView {
List {
NavigationLink("Link") {
NavigationStack {
Text("Destination")
}
}
}
} detail: {
}
}
}
When running the code above, as I tap the "Link", it navigate to "Destination" as expected. However, if I navigate back and tap again, this link won't navigate.
Is there any workaround? Thanks for your solution!

Related

SwiftUI bug with navigation view rotation?

While testing my app to make sure it behaves properly under screen rotations, I discovered that navigation links do not work after a certain sequence of rotations.
The following is a MWE:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink("link") {
Text("hi")
}
}
}
// rotate -> back -> link -> rotate -> back -> link
}
On a iPhone 13 Pro Max (iOS 15.2 (19C51)) simulator, the following leads to an error:
Run the app on the portrait mode
Rotate the app to the landscape mode
Touch the back button (in the navigation bar)
Touch the navigation link link
Rotate the app to the portrait mode
Touch the back button
Now touching the navigation link link does not work!
Also, the debug console prints:
Unbalanced calls to begin/end appearance transitions for <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentGS1_VVS_22_VariadicView_Children7ElementVS_24NavigationColumnModifier_GVS_18StyleContextWriterVS_19SidebarStyleContext___: 0x152f24860>.
Is this a bug in SwiftUI?
And is there a way to work around this issue?
I'm on macOS Monterey (12.2 (21D49)) + Xcode 13.2.1 (13C100).
Changing ColumnNavigationViewStyle to StackNavigationViewStyle will solve your problem, the sequence you mentioned is most probably a bug, hopefully apple will solve it soon.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink("link") {
Text("hi")
}
}
.navigationViewStyle(.stack) //Style
}
}
Without having to give up double column layout in iPadOS, I used the following conditional view builder to use different navigation style based on the horizontal size class.
(Although conditional view builders often do not play well with animations, it seems ok to use it here.)
extension View {
#ViewBuilder func `if`<TrueContent: View>(
_ condition: Bool,
then trueContent: (Self) -> TrueContent
) -> some View {
if condition {
trueContent(self)
} else {
self
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink("link") {
Text("hi")
}
}
.if(UIDevice.current.userInterfaceIdiom == .phone) {
$0.navigationViewStyle(.stack)
}
}
}
P.S. The issue can be reproduced in iPadOS as well when the scene is resized appropriately after each rotation, but I think this is less likely to happen without intention to reproduce.
The max uses columns in landscape and you are missing the second column View. Try this
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink("link") {
Text("hi")
}
Text("Select a link")
}
}
}

TabItem switching not working properly in iOS13 devices

I have created demo of Tab bar with two tabItems. Each tab-item have view controller in it.
On switching tabs, it is not showing its respective UI.
This is happening on iOS 13 only.
Code:
struct ContentView: View {
private enum Tab: Hashable {
case one
case two
}
#State private var selectedIndex = Tab.one
var body: some View {
TabView(selection: $selectedIndex) {
DetestVXViewController(bgColor: .red).tabItem {
Text("123")
}.tag(Tab.one)
DetestVXViewController(bgColor: .green).tabItem {
Text("321")
}.tag(Tab.two)
}
}
}
====
Attaching images of iOS 13 and 15 both.
Using tag seems overkill unless you need to keep track of said tab for quick actions or another purpose. You can still use tag in this though if needed. You can use #State property for the selection also if needed. You can easily switch tabs with the following example.
struct ContentView: View {
var body: some View {
TabView {
ForEach(1..<3) { index in
SomeView(title: "\(index)")
.tabItem {
Image(systemName: "\(index).circle.fill")
Text("\(index)")3
}
//.tag(index)
}
}
}
}

SwiftUI prevent List in Sheet from inheriting parent Form styles

How can I prevent a view within a Sheet from inheriting components from its parents?
I believed a Sheet would create a whole new view hierarchy, but apparently it does not completely. As seen in the Gif below, the ListView inherits the Form when it is attached to the Button inside the Form. When placing it outside the form, the List behaves as expected. Is there a way to prevent this from happening? As you can see, the list with its sections becomes pretty useless when inheriting the Form. This almost seems like a bug to me.
Tested in an empty project using Xcode Version 12.5 (12E262) and iOS 14.5
import SwiftUI
struct ContentView: View {
#State var isSheetPresented: Bool = false
var body: some View {
NavigationView {
Form {
NavigationLink ("Link", destination: ListView())
Button("Sheet") {
isSheetPresented = true
}
.sheet(isPresented: $isSheetPresented, content: {
NavigationView {
ListView()
}
})
}
}
}
}
struct ListView: View {
var body: some View {
List {
Button("1") { }
Button("2") { }
Button("3") { }
Section(header: Text("one")) {
Button("4") { }
}
}
.navigationBarTitle("List")
}
}
Use DefaultListStyle()
ListView().listStyle(DefaultListStyle())

SwiftUI Navigation Bar Title

I'm making my very first steps into SwiftUI following HackingWithSwift course. I have a problem trying to implement navigation bar in my app. For some reason the title does not show up.
Here's the code I'm using:
struct ContentView: View {
var body: some View {
NavigationView {
Form {
...
}
}
.navigationBarTitle(Text("WeSplit"))
}
}
Once running it in simulator or on my target device I see free space for the navigation bar title but no text there.
I've also tried typing .navigationBarTitle("WeSplit") with no Text() in there. Result's still the same.
Do you have any ideas on how to fix it? Thank you in advance!
I'm running Xcode Version 12.3.
It should be inside NavigationView, like
struct ContentView: View {
var body: some View {
NavigationView {
Form {
...
}
.navigationBarTitle(Text("WeSplit")) // << here !!
}
}
}

Why is SwiftUI picker in form repositioning after navigation?

After clicking the picker it navigates to the select view. The item list is rendered too far from the top, but snaps up after the animation is finished. Why is this happening?
Demo: https://gfycat.com/idioticdizzyazurevase
I already created a minimal example to rule out navigation bar titles and buttons, form sections and other details:
import SwiftUI
struct NewProjectView: View {
#State var name = ""
var body: some View {
NavigationView {
Form {
Picker("Client", selection: $name) {
Text("Client 1")
Text("Client 2")
}
}
}
}
}
struct NewProjectView_Previews: PreviewProvider {
static var previews: some View {
NewProjectView()
}
}
This happens in preview mode, simulator and on device (Xcode 11.2, iOS 13.2 in simulator, 13.3 beta 1 on device).
The obviously buggy behavior can be worked around when forcing the navigation view style to stacked:
NavigationView {
…
}.navigationViewStyle(StackNavigationViewStyle())
This is a solution to my problem, but I won‘t mark this as accepted answer (yet).
It seems to be a bug, even if it may be triggered by special circumstances.
My solution won‘t work if you need another navigation view style.
Additionally, it won‘t fix the horizontal repositioning mentioned by DogCoffee in the comments.
In my opinion, it has something to do with the navigation bar. In default (no mention of .navigationBarTitle extension), the navigation display mode is set to .automatic, this should be amend to .inline. I came across another post similar to this and use their solution to combine with yours, by using .navigationBarTitle("", displayMode: .inline) should help.
import SwiftUI
struct NewProjectView: View {
#State var name = ""
var body: some View {
NavigationView {
Form {
Picker("Client", selection: $name) {
Text("Client 1")
Text("Client 2")
}
}
.navigationBarTitle("", displayMode: .inline)
}
}
}
struct NewProjectView_Previews: PreviewProvider {
static var previews: some View {
NewProjectView()
}
}
Until this bug is resolved another way to work around this issue while retaining the DoubleColumnNavigationViewStyle for iPads would be to conditionally set that style:
let navView = NavigationView {
…
}
if UIDevice.current.userInterfaceIdiom == .pad {
return AnyView(navView.navigationViewStyle(DoubleColumnNavigationViewStyle()))
} else {
return AnyView(navView.navigationViewStyle(StackNavigationViewStyle()))
}
Thanks for this thread everyone! Really helped me understand things more and get a hold of one of my problems. To share with others, I was having this problem to but I was also having this problem when I set a section to appear in a if/else statement set on a section with a toggle. When the toggle was activated it would shift the section header horizontally a few pixels.
The following is how I fixed it
Section(header: Text("Subject Identified").listRowInsets(EdgeInsets()).padding(.leading)) {
Picker(selection: $subIndex, label: Text("Test")) {
ForEach(0 ..< subIdentified.count) {
Text(self.subIdentified[$0]).tag($0)
}
}
.labelsHidden()
.pickerStyle(SegmentedPickerStyle())
I'm still having horizontal shift on my picker selection view and not sure how to fix. I created another thread to received input. Thanks again! SwiftUI Shift Picker Text Horizontal

Resources