如何在SwiftUI NavigationStack中导航子视图且避免重复初始化
导航目标
- 功能1:
SelectDepartureArrivalView->CommutingView->ArrivedView - 功能2:
ScheduleView
功能说明:功能1包含3个视图,功能2仅包含1个视图。
预期与实际结果
- 预期:每个视图仅初始化一次
- 实际结果:每次导航操作都会触发视图初始化,更严重的是进入
ArrivedView时存在2个存活的SelectDepartureArrivalViewModel实例。
复现代码
// // ContentView.swift // NavigationStackExample // // Created by Jason Rich Darmawan Onggo Putra on 29/07/23. // import SwiftUI enum Page { // 功能1 case SelectDepartureArrivalView case CommutingView case ArrivedView // 功能2 case ScheduleView } class SelectDepartureArrivalViewModel: ObservableObject { init() { print("\(type(of: self)) \(#function)") } deinit { print("\(type(of: self)) \(#function)") } } struct SelectDepartureArrivalView: View { @EnvironmentObject var coordinator: Coordinator @ObservedObject var viewModel: SelectDepartureArrivalViewModel init(viewModel: SelectDepartureArrivalViewModel = SelectDepartureArrivalViewModel()) { self.viewModel = viewModel print("\(type(of: self)) \(#function)") } var body: some View { VStack { Text("SelectDepartureArrivalView") Button { coordinator.path.append(.CommutingView) } label: { Text("模拟通勤") } } .onAppear { print("\(type(of: self)) appear ")} .onDisappear { print("\(type(of: self)) disappear") } } } class CommutingViewModel: ObservableObject { init() { print("\(type(of: self)) \(#function)") } deinit { print("\(type(of: self)) \(#function)") } } struct CommutingView: View { @EnvironmentObject var coordinator: Coordinator @ObservedObject var viewModel: CommutingViewModel init(viewModel: CommutingViewModel = CommutingViewModel()) { self.viewModel = viewModel print("\(type(of: self)) \(#function)") } var body: some View { VStack { Text("CommutingView") Button { coordinator.path.append(.ArrivedView) } label: { Text("模拟到达") } } .onAppear { print("\(type(of: self)) appear ")} .onDisappear { print("\(type(of: self)) disappear") } } } class ArrivedViewModel: ObservableObject { init() { print("\(type(of: self)) \(#function)") } deinit { print("\(type(of: self)) \(#function)") } } struct ArrivedView: View { @ObservedObject var viewModel: ArrivedViewModel @EnvironmentObject var coordinator: Coordinator init(viewModel: ArrivedViewModel = ArrivedViewModel()) { self.viewModel = viewModel print("\(type(of: self)) \(#function)") } var body: some View { VStack { Text("ArrivedView") Button { coordinator.path = .init() } label: { Text("模拟退出") } } .onAppear { print("\(type(of: self)) appear ")} .onDisappear { print("\(type(of: self)) disappear") } } } class ScheduleViewModel: ObservableObject { init() { print("\(type(of: self)) \(#function)") } deinit { print("\(type(of: self)) \(#function)") } } struct ScheduleView: View { @ObservedObject var viewModel: ScheduleViewModel init(viewModel: ScheduleViewModel = ScheduleViewModel()) { self.viewModel = viewModel print("\(type(of: self)) \(#function)") } var body: some View { VStack { Text("ScheduleView") } .onAppear { print("\(type(of: self)) appear ")} .onDisappear { print("\(type(of: self)) disappear") } } } class Coordinator: ObservableObject { @Published var path: [Page] init(path: [Page] = [Page]()) { self.path = path } } struct ContentView: View { @StateObject private var coordinator: Coordinator init(coordinator: Coordinator = Coordinator()) { self._coordinator = StateObject(wrappedValue: coordinator) print("\(type(of: self)) \(#function)") } var body: some View { NavigationStack(path: $coordinator.path) { VStack { Button { coordinator.path.append(.SelectDepartureArrivalView) } label: { Text("模拟选择出发/到达") } Button { coordinator.path.append(.ScheduleView) } label: { Text("模拟跳转到日程视图") } } .navigationDestination(for: Page.self) { page in switch page { case .SelectDepartureArrivalView: SelectDepartureArrivalView() case .CommutingView: CommutingView() case .ArrivedView: ArrivedView() case .ScheduleView: ScheduleView() } } } .onAppear { print("\(type(of: self)) appear ")} .onDisappear { print("\(type(of: self)) disappear") } .environmentObject(coordinator) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
操作日志与状态
步骤1:Root -> coordinator.path.append(.SelectDepartureArrivalView)
ContentView init(coordinator:) ContentView appear SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) SelectDepartureArrivalView appear SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) SelectDepartureArrivalViewModel deinit
当前状态:
- 1个ContentView处于显示状态
- 1个SelectDepartureArrivalViewModel实例存活
- 1个SelectDepartureArrivalView处于显示状态
步骤2:SelectDepartureArrivalView -> coordinator.path.append(.CommutingView)
SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) CommutingViewModel init() CommutingView init(viewModel:) CommutingView appear SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) CommutingViewModel init() CommutingView init(viewModel:) SelectDepartureArrivalViewModel deinit SelectDepartureArrivalViewModel deinit CommutingViewModel deinit SelectDepartureArrivalView disappear
当前状态:
- 1个ContentView处于显示状态
- 1个SelectDepartureArrivalViewModel实例存活
- 1个CommutingViewModel实例存活
- 1个CommutingView处于显示状态
步骤3:CommutingView -> coordinator.path.append(.ArrivedView)
SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) CommutingViewModel init() CommutingView init(viewModel:) ArrivedViewModel init() ArrivedView init(viewModel:) ArrivedView appear SelectDepartureArrivalViewModel init() SelectDepartureArrivalView init(viewModel:) CommutingViewModel init() CommutingView init(viewModel:) ArrivedViewModel init() ArrivedView init(viewModel:) CommutingViewModel deinit CommutingViewModel deinit ArrivedViewModel deinit SelectDepartureArrivalViewModel deinit CommutingView disappear
当前状态:
- 1个ContentView处于显示状态
- 2个SelectDepartureArrivalViewModel实例存活
- 1个CommutingViewModel实例存活
- 1个ArrivedViewModel实例存活
- 1个ArrivedView处于显示状态
步骤4:ArrivedView -> coordinator.path = .init()
SelectDepartureArrivalViewModel deinit SelectDepartureArrivalViewModel deinit CommutingViewModel deinit ArrivedView disappear ArrivedViewModel deinit
当前状态:
- 1个ContentView处于显示状态
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

