iOS13下SwiftUI中如何仅初始化一次ContainerViewModel?
解决iOS13下ViewModel重复初始化的问题
核心思路
由于iOS13不支持StateObject,我们需要将ContainerViewModel的实例提前创建并保存在父视图的状态中,避免每次视图重建时重新初始化。这样无论tab切换还是showHeader状态变化,ViewModel实例都会保持存活,网络请求只会执行一次。
具体修改步骤
- 在MainView中预创建并保存ViewModel实例
用@State存储三个ContainerViewModel实例,iOS13中@State可以维持引用类型的生命周期,不会因body重建而销毁实例:
struct MainView: View { @EnvironmentObject var viewModel: MainViewModel @State private var selectedCategory: CategoryType = .first // 预创建三个ViewModel实例,通过@State保持生命周期 @State private var firstVM = ContainerViewModel(selectedCategory: .first) @State private var secondVM = ContainerViewModel(selectedCategory: .second) @State private var thirdVM = ContainerViewModel(selectedCategory: .third) var body: some View { HeaderView() { category in selectedCategory = category } TabView(selection: $selectedCategory) { // 传递预创建的ViewModel,而非每次新建 ContainerView() .environmentObject(firstVM) .tag(CategoryType.first) ContainerView() .environmentObject(secondVM) .tag(CategoryType.second) ContainerView() .environmentObject(thirdVM) .tag(CategoryType.third) } } }
- 修正ContainerView的递归问题
你原代码中ContainerView内部嵌套了自身,会导致无限递归,调整为实际内容展示逻辑:
struct ContainerView: View { @EnvironmentObject var viewModel: ContainerViewModel @State private var showHeader = true var body: some View { VStack { if showHeader { HeaderView() { category in viewModel.selectedCategory = category } } // 替换为你实际的内容视图,比如分类列表 Text("当前分类: \(viewModel.selectedCategory.rawValue)") } } }
- 确保网络请求仅执行一次
将网络请求放在ContainerViewModel的初始化方法中,由于实例只会创建一次,请求也只会执行一次:
class ContainerViewModel: ObservableObject { @Published var selectedCategory: CategoryType init(selectedCategory: CategoryType) { self.selectedCategory = selectedCategory // 网络请求仅在ViewModel初始化时执行一次 fetchCategoryData() } private func fetchCategoryData() { // 执行具体的网络请求操作 print("开始请求分类\(selectedCategory)的数据") } }
可选优化:延迟加载ViewModel
如果不想初始时就创建三个ViewModel,可以通过onAppear延迟初始化,仅在对应tab首次被选中时创建实例:
struct MainView: View { @EnvironmentObject var viewModel: MainViewModel @State private var selectedCategory: CategoryType = .first @State private var firstVM: ContainerViewModel? @State private var secondVM: ContainerViewModel? @State private var thirdVM: ContainerViewModel? var body: some View { HeaderView() { category in selectedCategory = category } TabView(selection: $selectedCategory) { ContainerView() .environmentObject(firstVM ?? .init(selectedCategory: .first)) .onAppear { if firstVM == nil { firstVM = ContainerViewModel(selectedCategory: .first) } } .tag(CategoryType.first) ContainerView() .environmentObject(secondVM ?? .init(selectedCategory: .second)) .onAppear { if secondVM == nil { secondVM = ContainerViewModel(selectedCategory: .second) } } .tag(CategoryType.second) ContainerView() .environmentObject(thirdVM ?? .init(selectedCategory: .third)) .onAppear { if thirdVM == nil { thirdVM = ContainerViewModel(selectedCategory: .third) } } .tag(CategoryType.third) } } }
内容的提问来源于stack exchange,提问作者wearenotchampions
相关产品推荐
相关产品推荐

