SwiftUI中是否必须使用ObservableObject?结构体能否直接作为视图状态
核心结论
你完全不需要强制将自定义结构体包裹在类类型的ViewModel中使用,SwiftUI的@State属性包装器原生支持结构体、枚举等值类型,本身就是为存储视图本地值类型状态设计的。
你当前代码的问题
你现在的实现无法正常同步状态,和用不用结构体无关,核心是属性包装器用错了:
- 子视图
NumberView中用@State标记number,意味着NumberView会自己持有一份独立的number状态 - 你初始化
NumberView时传入的object.x/object.y/object.z是值类型的拷贝,子视图里修改Stepper、TextField时,改动的是子视图自己持有的副本,根本不会同步回外层ObjectInspector的object属性。
结构体状态的正确用法
如果Object3D是ObjectInspector视图的本地私有状态,不需要跨多页面共享、不需要承载脱离视图层的复杂业务逻辑,直接用@State存结构体即可,只需要做两处修改:
- 把
NumberView的@State替换为@Binding,表示这个属性由父视图传入,是双向绑定的引用,不是本地独立状态:
struct NumberView<Number : Strideable> : View { var label : String @Binding var number : Number var body : some View { HStack { TextField( self.label, value: self.$number, formatter: NumberFormatter() ) Stepper("", value: self.$number) .labelsHidden() } } }
- 父视图调用
NumberView时,传入状态的绑定(值前加$),而非直接传值:
HStack { NumberView(label: "X:", number: $object.x) NumberView(label: "Y:", number: $object.y) NumberView(label: "Z:", number: $object.z) }
改完后所有对x/y/z/visible属性的修改都会直接同步到外层的@State var object: Object3D,SwiftUI会自动在结构体属性变更时重绘所有依赖该状态的视图,不需要额外写任何刷新逻辑,也不需要引入类类型的ViewModel。
什么时候才需要用遵守
ObservableObject的类类型ViewModel 只有遇到以下场景时,才需要把状态封装到类类型的ObservableObject中:
- 状态需要在多个无直接父子关系的视图、跨页面/跨模块共享(比如全局登录状态、全局配置)
- 状态对应的业务逻辑需要脱离视图层存在(比如网络请求、数据库读写、复杂业务校验逻辑,不适合写在View的body或生命周期回调中)
- 需要做更细粒度的更新性能控制:
@State持有结构体时,只要结构体任意属性变更,就会触发持有它的视图更新;而ObservableObject可以通过@Published、自定义objectWillChange触发时机,精确控制哪些状态变更会触发视图刷新,优化复杂界面的性能。
注意:如果属性包装器用法错误,哪怕把结构体包裹在类ViewModel中,依然会出现状态不同步、视图不刷新的问题,这和值类型/引用类型本身没有必然关系。
内容的提问来源于stack exchange,提问作者Ter Maxima
相关产品推荐
相关产品推荐

