NavigationLink срабатывает более одного раза в NavigationStack
Я столкнулся с проблемой, когдаNavTestChildView
звонил еще один раз. Я не понимаю, что происходит не так. Я тестировал на реальном устройстве с iOS 16.0.3 и эмулятором Xcode 14.0.1.
Я заменил исходный код, чтобы дать больше информации об архитектуре, почему я создаюNavTestService
вnavigationDestination
.
enum NavTestRoute: Hashable {
case child(Int)
}
class NavTestService: ObservableObject {
let num: Int
init(num: Int) {
self.num = num
print("[init][NavTestService]")
}
deinit {
print("[deinit][NavTestService]")
}
}
struct NavTestChildView: View {
@EnvironmentObject var service: NavTestService
init() {
print("[init][NavTestChildView]")
}
var body: some View {
Text("NavTestChildView \(service.num)")
}
}
struct NavTestMainView2: View {
var body: some View {
VStack {
ForEach(1..<10, id: \.self) { num in
NavigationLink(value: NavTestRoute.child(num)) {
Text("Open child \(num)")
}
}
}
}
}
struct NavTestMainView: View {
var body: some View {
NavigationStack {
NavTestMainView2()
.navigationDestination(for: NavTestRoute.self) { route in
switch route {
case let .child(num):
NavTestChildView().environmentObject(NavTestService(num: num))
}
}
}
}
}
журналы:
[init][NavTestChildView]
[init][NavTestService]
[deinit][NavTestService]
[init][NavTestChildView]
[init][NavTestService]
2 ответа
Похоже, есть период, когда экземплярNavTestService
никем не удерживается и уходит из кучи. На практике это вряд ли произойдет, потому что.environmentObject
vars обычно хранятся где-то вверх по иерархии. Если вы изменитеNavTestMainView
соответственно:
struct NavTestMainView: View {
let navTestService = NavTestService()
var body: some View {
NavigationStack {
NavigationLink(value: NavTestRoute.child) {
Text("Open child")
}
.navigationDestination(for: NavTestRoute.self) { route in
switch route {
case .child:
NavTestChildView().environmentObject(navTestService)
}
}
}
}
}
... вы не получитеdeinit
с и без лишнегоinit
также. Консоль выведет:
[init()][NavTestService]
[init()][NavTestChildView]
[init()][NavTestChildView]
Также обратите внимание, что если вы закомментируетеlet navTestService = NavTestService()
и завернутьNavTestChildView().environmentObject(NavTestService())
в LazyView вы получите следующий вывод:
[init()][NavTestChildView]
[init()][NavTestService]
ГдеLazyView
является:
struct LazyView<Content: View>: View {
let build: () -> Content
init(_ build: @autoclosure @escaping () -> Content) {
self.build = build
}
var body: Content {
build()
}
}
Это не "выстрел", это просто несколько раз запускает структуру View, что совершенно нормально и практически не требует дополнительных затрат, поскольку структуры View являются типами значений. Обычно это происходит из-за того, что дизайн UIKit, управляемый событиями, плохо согласуется с дизайном SwiftUI, управляемым состоянием.
Вы можете упростить свой код, заменив оператор перечисления / случая маршрутизатора несколькимиnavigationDestination
для каждого типа модели.