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

SwiftUI中是否必须使用ObservableObject?结构体能否直接作为视图状态

核心结论

你完全不需要强制将自定义结构体包裹在类类型的ViewModel中使用,SwiftUI的@State属性包装器原生支持结构体、枚举等值类型,本身就是为存储视图本地值类型状态设计的。

你当前代码的问题

你现在的实现无法正常同步状态,和用不用结构体无关,核心是属性包装器用错了:

  • 子视图NumberView中用@State标记number,意味着NumberView会自己持有一份独立的number状态
  • 你初始化NumberView时传入的object.x/object.y/object.z是值类型的拷贝,子视图里修改Stepper、TextField时,改动的是子视图自己持有的副本,根本不会同步回外层ObjectInspector的object属性。
结构体状态的正确用法

如果Object3D是ObjectInspector视图的本地私有状态,不需要跨多页面共享、不需要承载脱离视图层的复杂业务逻辑,直接用@State存结构体即可,只需要做两处修改:

  1. 把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()
        }
    }
}
  1. 父视图调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:51:08