admin 管理员组文章数量: 1086019
I have seen many questions where this happens and they are using NavigationLink(destination:
but this case here feels unique. Apologies because it is a bit convoluted but what is interesting is there are a multiple different ways to "fix" the problem.
In the example code when you select a Dog
NavigationLink
the app opens that Dog
double deep. Root->Dog(id: 0, "Spot")->Dog(id: 0, "Spot")
I know this set up is a bit ridiculous but I have narrowed this down to its most minimal form.
As far as models we have
struct Dog: Identifiable, Hashable {
var id = UUID()
var name: String
}
struct Cat: Identifiable, Hashable {
var id = UUID()
var name: String
}
class NavigationController: ObservableObject {
@Published var path = NavigationPath()
}
class Model: ObservableObject {
@Published var dogs: [Dog] = []
@Published var isLoading: Bool = false
func loadDogs() {
guard dogs.isEmpty else { return }
Task { @MainActor in
isLoading = true
try? await Task.sleep(for: .seconds(1))
self.dogs = [Dog(name: "Fifi"), Dog(name: "Spot")]
isLoading = false
}
}
}
As far as views we have
struct ContentView: View {
@StateObject private var navigationController = NavigationController()
@StateObject private var model = Model()
var body: some View {
TabView {
MainView(model: model)
.tabItem {
Label("Analytics", systemImage: "chart.bar")
}
}
.environmentObject(navigationController)
}
}
struct MainView: View {
@EnvironmentObject var navigationController: NavigationController
@ObservedObject public var model: Model
var body: some View {
NavigationStack(path: $navigationController.path) {
if model.isLoading {
ProgressView()
} else {
List {
Section("Good Boys") {
ForEach(model.dogs) { dog in
NavigationLink(value: dog) {
Text(dog.name)
}
}
}
}
.navigationTitle("Dogs")
.navigationDestination(for: Dog.self) { dog in
Text(dog.name)
}
.navigationDestination(for: Cat.self) { cat in
CatScreen(cat: cat, model: model)
}
}
}
.onAppear {
model.loadDogs()
}
}
}
Whenever you press the NavigationLink
the view gets pushed into the stack twice. There are a couple ways to prevent this. And they all work independently:
- You can get rid of the
NavigationController
and@Environment
and just have the path be a@State
inMainView
- Get rid of the
TabView
inMainView
(yes I know in this example there is only one tab. In my app there are multiple so removing this is not an option) - Get rid of the
navigationDestination
forCat
(In my app there is a section where you can select a cat but again this is over simplified) - Remove the model from CatScreen(). If the view built in the
navigationDestination
does not take inmodel
for some reason everything works fine. - Remove the if branch with the
ProgressView
. Similarly you can put that whole thing in aGroup
and put thenavigationDestination
up there.
5 is the only solution that really makes sense to me. But even then I do not think this behavior is documented?
In the documentation Apple says
Add this view modifier to a view inside a NavigationStack to describe the view that the stack displays when presenting a particular kind of data.
Do not put a navigation destination modifier inside a “lazy” container, like List or LazyVStack. These containers create child views only when needed to render on screen. Add the navigation destination modifier outside these containers so that the navigation stack can always see the destination.
However in this case there is no empty container. I understand under the hood I get a
/// Provides support for "if" statements in multi-statement closures, producing
/// ConditionalContent for the "then" branch.
public static func buildEither<TrueContent, FalseContent>(first: TrueContent) -›
_ConditionalContent<TrueContent, FalseContent> where TrueContent : View, FalseContent: View
But even given that the navigationDestination
and the structured path should be visible and stable no matter the value of loading. They would've needed to mention in the documentation that it does not work for conditional content. I also tested and there seems to be no reason that navigationDestination needs to be in the top view under a NavigationStack
.
Also why do 1-4 change anything here?
本文标签:
版权声明:本文标题:swift - NavigationLink with value pushes navigation stack twice on press. Why are there four different ways to fix it? - Stack O 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1744074358a2529070.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论