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

iOS13下SwiftUI中如何仅初始化一次ContainerViewModel?

解决iOS13下ViewModel重复初始化的问题

核心思路

由于iOS13不支持StateObject,我们需要将ContainerViewModel的实例提前创建并保存在父视图的状态中,避免每次视图重建时重新初始化。这样无论tab切换还是showHeader状态变化,ViewModel实例都会保持存活,网络请求只会执行一次。

具体修改步骤

  1. 在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)
        }
    }
}
  1. 修正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)")
        }
    }
}
  1. 确保网络请求仅执行一次
    将网络请求放在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:52:38