FlowStacks
FlowStacks copied to clipboard
NavigationView state gets reset when presenting and dismissing view on NavigationView
Thank you for this excellent framework!
We have found an issue when using an UIViewControllerRepresentable with a popover. The UIVIewController present a popover when it appears. After clicking on the popover and dismissing it, a new rendering cycle starts and the NavigationView
pops back to its root view.
We have tried to debug the issue and set a breakpoint on the switch
statement. The routes
just hold a single element instead of 2 routes (root and the pushed view). It seems that after the routes object is updated no rendering cycle occurs were there are 2 elements in routes
.
In another debugging attempt we replaced the routes with a ButtonView and everything works fine. However also here we have seen that the routes
object has 1 element after init (when evaluating it at the before mentioned breakpoint), after pushing a 2nd view it still shows only 1 element, after another push there are 3 elements in the routes object.
public struct Content: View {
@State private var routes: Routes<Screen>
let onDismissTapped: (() -> Void)
public var body: some View {
Router($routes) { screen, _ in
switch screen {
case .rootView:
let model = Model()
RootView(model: model)
case .pushedView:
let model = Model2(didSelectNextButton: clickedNext)
ViewRepresentableView(model: model) // this one is presenting a popover when it appeares
case .thirdView:
ThirdView()
}
}
.navigationBarTitle(Text(" "), displayMode: .inline)
}
}
struct ButtonView: View {
var action: () -> Void
let date = Date()
var body: some View {
Button("press me! \(date.asStringISO8601)") {
action()
}
}
}
Thanks for raising this issue @Kondamon! I haven't yet tried to reproduce the issue, but something seems strange in your example code:
let model = Model()
RootView(model: model)
Is model
an ObservableObject
? If so, I don't think it's a good idea to recreate it every time the coordinator's body is re-evaluated (which will happen frequently). Such a model should last from when its screen is first created to when it is destroyed. Otherwise, SwiftUI will consider the view to require re-rendering every time the body is evaluated. This can cause the strange behaviour you've noticed.
If you were to include the model as an associated value of your screen enum, then the same model instance will be used for that screen even when the body is re-evaluated, e.g.:
Router($routes) { screen, _ in
switch screen {
case .rootView(let model):
RootView(model: model)
case .pushedView(let model):
ViewRepresentableView(model: model)
case .thirdView:
ThirdView()
}
}