Skip to content

Instantly share code, notes, and snippets.

@helje5
Last active June 21, 2021 07:48
Show Gist options
  • Save helje5/38295beb7473a83ad7d236f99172d256 to your computer and use it in GitHub Desktop.
Save helje5/38295beb7473a83ad7d236f99172d256 to your computer and use it in GitHub Desktop.
Sample showing how programmatic SwiftUI navigation doesn't work and ignores the source of truth.
// Created by Miroslav Djukic on 9.6.21.
import SwiftUI
struct AView: View {
@EnvironmentObject var navigatonState: NavigationState
var body: some View {
NavigationView {
VStack {
Text("AView")
Spacer().frame(height: 30)
NavigationLink("Next", destination: BView(), isActive: $navigatonState.bViewActive)
Button("Go to D", action: navigatonState.navigateToDView)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.black)
}
}
}
struct BView: View {
@EnvironmentObject var navigatonState: NavigationState
var body: some View {
VStack {
Text("BView")
Spacer().frame(height: 30)
NavigationLink("Next", destination: CView(), isActive: $navigatonState.cViewActive)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
}
}
struct CView: View {
@EnvironmentObject var navigatonState: NavigationState
var body: some View {
VStack {
Text("CView")
Spacer().frame(height: 30)
NavigationLink("Next", destination: DView(), isActive: $navigatonState.dViewActive)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
}
}
struct DView: View {
var body: some View {
VStack {
Text("DView")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
}
}
final class NavigationState: ObservableObject {
@Published var bViewActive = false
@Published var cViewActive = false
@Published var dViewActive = false
// This doesn't work normal with any delay less then 0.5s, toggle if/else below to see
func navigateToDView() {
#if true // doesn't work
bViewActive = true
cViewActive = true
dViewActive = true
#else // works
bViewActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
self?.cViewActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self?.dViewActive = true
}
}
#endif
}
}
@helje5
Copy link
Author

helje5 commented Jun 10, 2021

Full project by @DjukicMiroslav over here: RouterTest

FB9148980

The behaviour is slightly different between Xcode 12 & 13 but doesn't fully work in either.

The core issue seems to be that NavigationLink upon creation doesn't bother visiting the source of truth.

@helje5
Copy link
Author

helje5 commented Jun 10, 2021

This has a sequence of pages: A => B => C => D. The goal is to directly jump to D (usually w/o animation, but depends).
The truth which link is active is stored in an observable object. This is then bound to the NavigationLink isActive.

What happens? Seems to depends on the SwiftUI version and the moon phase.

  • sometimes it jumps to just B and stays there. When pressing "next", it jumps to D, but then pops back to C, then B.
  • SwiftUI 3 sometimes crashes with an Env Object not being bound, this can be fixed by explicitly passing it along (in the destination view, .environmentObject(navState).
  • if this is done, pressing "go to d", jumps to B, then C, then D, then pops to C, and then pops to B

@helje5
Copy link
Author

helje5 commented Jun 11, 2021

Someone suggested adding:

NavigationView {
   ...
}
.navigationViewStyle(StackNavigationViewStyle())

but that doesn't help. In a 14.5 simulator it still just jumps to B.

@zntfdr
Copy link

zntfdr commented Jun 21, 2021

My FB# on the matter: FB9197698

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment