SwiftUI中@Environment(\.dismiss)配合自定义init报变量未初始化错误
错误产生原因
这个报错的提示信息存在误导,实际问题来自SwiftUI属性包装器的初始化规则,以及Xcode 13系列版本的编译器提示缺陷:
@State、@Environment等SwiftUI属性包装器不会直接把值存在你声明的变量上,每个被包装的属性都会自动生成一个带下划线前缀的底层存储属性(比如声明@State private var myInt: Int,会自动生成_myInt,类型为State<Int>),你代码中直接写的myInt本质是访问底层存储的计算属性。- 自定义
init中直接写self.myInt = foo,本质是调用该计算属性的setter。Swift要求调用实例方法/访问计算属性前,必须完成所有存储属性的初始化。但@Environment的底层存储由SwiftUI在视图挂载时自动注入,自定义init中编译器不会为它合成初始化逻辑,导致编译器判定self未完成初始化,最终抛出了指向myInt的错误提示。 - 该错误提示错位的问题在Xcode 14及后续版本已经修复,会明确提示开发者需要初始化@State对应的底层存储属性。
修复方案
方案1(推荐,适配Xcode 13.4.1)
直接在init中初始化@State对应的底层存储属性,无需手动处理@Environment的存储逻辑:
struct ChildView: View { @Environment(\.dismiss) var dismiss @State private var myInt: Int init(foo: Int) { self._myInt = State(initialValue: foo) } var body: some View { Text("Hello, world! myInt = \(myInt)") .padding() Button(action: { dismiss() }, label: { Text("Dismiss") }) } }
该方案无需改动原有业务逻辑,仅调整@State的初始化写法即可解决问题,兼容性最好。
方案2(适合复杂业务场景)
将外部传入参数和内部@State状态做隔离,不在init中直接操作@State属性:
struct ChildView: View { @Environment(\.dismiss) var dismiss @State private var myInt: Int = 0 private let inputFoo: Int init(foo: Int) { self.inputFoo = foo } var body: some View { Text("Hello, world! myInt = \(myInt)") .padding() .onAppear { myInt = inputFoo } Button(action: { dismiss() }, label: { Text("Dismiss") }) } }
该写法明确区分了外部不可变入参和内部可变视图状态,后续扩展首屏加载逻辑时更易维护。
内容的提问来源于stack exchange,提问作者xvorsx
相关产品推荐
相关产品推荐

