You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 01:27:37