I want to programmatically be able to navigate to a link within a List
of NavigationLinks
when the view appears (building deep linking from push notification). I have a string -> Bool dictionary which is bound to a custom Binding<Bool>
inside my view. When the view appears, I set the bool property, navigation happens, however, it immediately pops back. I followed the answer in SwiftUI NavigationLink immediately navigates back and made sure that each item in the List
has a unique identifier, but the issue still persists.
Two questions:
import SwiftUI
class ContentViewModel: ObservableObject {
@Published var isLinkActive:[String: Bool] = [:]
}
struct ContentViewTwo: View {
@ObservedObject var contentViewModel = ContentViewModel()
@State var data = ["1", "2", "3"]
@State var shouldPushPage3: Bool = true
var page3: some View {
Text("Page 3")
.onAppear() {
print("Page 3 Appeared!")
}
}
func binding(chatId: String) -> Binding<Bool> {
return .init(get: { () -> Bool in
return self.contentViewModel.isLinkActive[chatId, default: false]
}) { (value) in
self.contentViewModel.isLinkActive[chatId] = value
}
}
var body: some View {
return
List(data, id: \.self) { data in
NavigationLink(destination: self.page3, isActive: self.binding(chatId: data)) {
Text("Page 3 Link with Data: \(data)")
}.onAppear() {
print("link appeared")
}
}.onAppear() {
print ("ContentViewTwo Appeared")
if (self.shouldPushPage3) {
self.shouldPushPage3 = false
self.contentViewModel.isLinkActive["3"] = true
print("Activating link to page 3")
}
}
}
}
struct ContentView: View {
var body: some View {
return NavigationView() {
VStack {
Text("Page 1")
NavigationLink(destination: ContentViewTwo()) {
Text("Page 2 Link")
}
}
}
}
}
SwiftUI's NavigationLink has a second initializer that has an isActive parameter, allowing us to read or write whether the navigation link is currently active. In practical terms, this means we can programmatically trigger the activation of a navigation link by setting whatever state it's watching to true.
The error is due to the lifecycle of the ViewModel, and is a limitation with SwiftUI NavigationLink itself at the moment, will have to wait to see if Apple updates the pending issues in the next release.
Update for SwiftUI 2.0
:
Change:
@ObservedObject var contentViewModel = ContentViewModel()
to:
@StateObject var contentViewModel = ContentViewModel()
@StateObject
means that changes in the state of the view model do not trigger a redraw of the whole body
.
You also need to store the shouldPushPage3
variable outside the View
as the view will get recreated every time you pop back to the root View
.
enum DeepLinking {
static var shouldPushPage3 = true
}
And reference it as follows:
if (DeepLinking.shouldPushPage3) {
DeepLinking.shouldPushPage3 = false
self.contentViewModel.isLinkActive["3"] = true
print("Activating link to page 3")
}
The bug got fixed with the latest SwiftUI release. But to use this code at the moment, you will need to use the beta version of Xcode and iOS 14 - it will be live in a month or so with the next GM Xcode release.
I was coming up against this problem, with a standard (not using 'isActive') NavigationLink - for me the problem turned out to be the use of the view modifiers: .onAppear{code} and .onDisappear{code} in the destination view. I think it was causing a re-draw loop or something which caused the view to pop back to my list view (after approx 1 second). I solved it by moving the modifiers onto a part of the destination view that's not affected by the code in those modifiers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With