基于@Observable ViewModel初始化@State属性selectedOption为何失败?
问题原因分析
你遇到的问题核心在于**@State属性的初始化时机限制**:
- @State是SwiftUI负责管理的属性包装器,视图的
init方法执行时,@State的底层存储还未被SwiftUI完全初始化。此时直接给selectedOption赋值,这个值不会被SwiftUI的状态系统保留,后续视图完成初始化后,@State会被重置为声明时的默认值(也就是nil)。 - 控制台能打印出匹配的Option实例,只是因为你直接访问了viewModel的计算结果,但这个实例并没有被@State的存储正确持有。
解决方案
方案1:通过onAppear延迟赋值
在视图完成初始化后(onAppear阶段),@State的存储已经就绪,此时赋值会被正确保留:
@State var selectedOption: Option? init() { // 保留打印调试代码,移除赋值逻辑 print(viewModel.options.count) print(viewModel.options.first { $0.identifier == Settings.currentLanguage }) print(viewModel.options.map { $0.identifier }) print(Settings.currentLanguage) } var body: some View { ZStack { ScrollView { // 原有视图内容... } .padding(20) .onAppear { // 在视图出现时完成赋值 selectedOption = viewModel.options.first { $0.identifier == Settings.currentLanguage } } BottomSheetView(isShowing: $isSelected) { PickerSelect(selectedOption: $selectedOption, options: viewModel.options) } } }
方案2:将状态移至ViewModel管理(更符合MVVM)
把selectedOption的状态放到ViewModel中,利用@Observable的特性管理,避免@State的初始化限制:
修改ViewModel:
@Observable class SettingsViewModel { let settings = Settings.shared var selectedOption: Option? var options: [Option] { Settings.languages.map { language in Option(title: settings.languageTitle(for: language), value: settings.keywordsTitle(for: language), identifier: language) } } init() { // 在ViewModel初始化时直接完成匹配赋值 selectedOption = options.first { $0.identifier == Settings.currentLanguage } } }
修改视图:
@StateObject var viewModel = SettingsViewModel() var body: some View { ZStack { ScrollView { VStack(spacing: 40) { InAppPurchaseView() HStack { TextLabel("settings.language".localized) Spacer() VStack(alignment: .leading) { // 直接使用ViewModel中的selectedOption TextLabel(viewModel.selectedOption?.title, size: 24, weight: .bold) TextLabel(viewModel.selectedOption?.value) } } .onTapGesture { isSelected = true } } } .padding(20) BottomSheetView(isShowing: $isSelected) { // 绑定ViewModel的selectedOption PickerSelect(selectedOption: $viewModel.selectedOption, options: viewModel.options) } } }
方案3:直接在@State声明时初始化
如果viewModel在视图声明@State前已经初始化完成,可以直接在@State的声明中赋值:
// 假设viewModel是@StateObject或已提前初始化 @StateObject var viewModel = SettingsViewModel() @State var selectedOption: Option? = SettingsViewModel().options.first { $0.identifier == Settings.currentLanguage } // 移除init中的赋值逻辑
注意:这种方式需要确保SettingsViewModel()的初始化不会产生额外副作用,且options的计算结果是稳定的。
内容的提问来源于stack exchange,提问作者kunass2
相关产品推荐
相关产品推荐

