SwiftUI中@Observable管理TabView选中项导致切换时重置内容
SwiftUI TabView 使用@Observable管理选中项时视图状态丢失的解决方法
问题核心
当用@Observable类Navigator管理selectedTab时,每次切换标签页都会触发ContentView的body重新计算,而PageOne和PageTwo的ViewModel是在body内直接初始化的,这会导致每次重建都生成新的ViewModel实例,之前的计数状态自然丢失。而用@State private var selectedTab时,SwiftUI会更高效地更新视图,不会触发ContentView的完全重建,TabView也会保留已加载页面的状态。
方案一:在ContentView中提前持有ViewModel实例
把ViewModel的创建移到ContentView的初始化阶段,用@State持有实例,这样body重建时不会重新创建ViewModel,保证状态保留:
import SwiftUI @Observable class Navigator { var selectedTab = Tab.one } enum Tab: Equatable { case one, two } struct ContentView: View { @State private var navigator = Navigator() // 提前创建ViewModel实例,用@State持有 @State private var pageOneVM = ViewModel(count: 0) @State private var pageTwoVM = ViewModel(count: 0) var body: some View { TabView(selection: $navigator.selectedTab) { PageOne(viewModel: pageOneVM) .tabItem { Text("one") } .tag(Tab.one) PageTwo(viewModel: pageTwoVM) .tabItem { Text("two") } .tag(Tab.two) } .padding() } } struct PageOne: View { var viewModel: ViewModel var body: some View { VStack { Button("increase page One") { viewModel.count += 1 } Text("Current count \(viewModel.count)") } } } @Observable class ViewModel { var count: Int init(count: Int) { self.count = count } } struct PageTwo: View { var viewModel: ViewModel var body: some View { VStack { Button("increase page two") { viewModel.count += 1 } Text("Current count \(viewModel.count)") } } }
方案二:让Page内部持有ViewModel实例
如果希望ViewModel由页面自己管理,也可以在PageOne和PageTwo内部用@State持有ViewModel,这样即使ContentView重建,页面的ViewModel实例也不会被重新创建:
// 修改PageOne和PageTwo的实现 struct PageOne: View { // 页面内部持有ViewModel @State private var viewModel = ViewModel(count: 0) var body: some View { VStack { Button("increase page One") { viewModel.count += 1 } Text("Current count \(viewModel.count)") } } } struct PageTwo: View { @State private var viewModel = ViewModel(count: 0) var body: some View { VStack { Button("increase page two") { viewModel.count += 1 } Text("Current count \(viewModel.count)") } } }
补充说明
- 用
@Observable类管理导航状态是合理的MVVM实践,关键是要避免在视图的body闭包内创建需要保留状态的对象,因为body可能会被SwiftUI频繁调用重建。 - 如果未来需要更复杂的导航路径管理,可以扩展
Navigator类,同时保证所有需要保留状态的ViewModel实例都被提前持有,而不是在body中临时创建。
内容的提问来源于stack exchange,提问作者ninja_iOS
相关产品推荐
相关产品推荐

