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

SwiftUI中@Observable视图模型搭配@State的使用时机及视图重建问题咨询

SwiftUI中@Observable视图模型搭配@State的使用时机及视图重建问题咨询

嘿,这个问题我之前也踩过一模一样的坑!明明代码结构看起来差不多,一个不用@State就能正常跑,另一个缺了@State就各种UI抽风,那种困惑感太真实了。咱们一点点拆解清楚:

首先得先搞懂核心逻辑:@Observable的视图模型如果是普通属性,它的生命周期是和视图实例绑定的——一旦SwiftUI把你的视图实例销毁重建,这个VM就会被重新初始化,之前的所有状态直接清零。而@State的作用,就是把VM的存储从视图实例里“挪”到SwiftUI自己管理的视图树状态容器里,不管视图怎么重建,VM的实例都会被稳稳保留住,状态也不会丢。

先聊聊你的两个例子为啥表现不一样

第一个AddProductView不用@State也正常,大概率是因为这个视图从来没被SwiftUI重建过——比如它可能是根视图,或者父视图的状态变化没触发它的销毁重建,VM的实例从始至终都是同一个,@Bindable能正常监听它的属性变化,UI自然能跟着更新。

而ProductDetailsView就不一样了:你在.onAppear里调用fetchProduct(),如果不用@State,视图很可能在.onAppear执行前后被重建了一次——旧的视图实例被销毁,新的视图实例带着全新初始化的VM出现,之前设置的showErrorAlert状态直接没了,UI当然就不更新,甚至看起来像是“重置”了。

哪些情况会触发SwiftUI重建你的视图?

这些场景是最常见的“坑点”:

  • 父视图的状态变化:比如父视图的@State、@Binding属性更新了,导致父视图重新计算body,这时候如果你的子视图是直接在父视图body里创建的(比如写ProductDetailsView()),SwiftUI很可能会销毁旧的子视图实例,创建新的,你的普通VM属性就跟着重置了。
  • 视图标识变化:如果你给视图加了.id()修饰符,当id的值变化时,SwiftUI会把这个视图当成完全全新的对象,直接销毁旧实例重建,VM自然也没了。
  • 导航栈操作:比如从导航栈返回再重新进入某个视图,SwiftUI有时候会把之前的视图实例彻底销毁,重新创建一个,这时候普通VM的状态就全丢了。
  • 设备布局变化:比如旋转设备、切换分屏模式,或者父视图的布局容器(比如从HStack改成VStack)发生变化,都可能触发视图树的调整,导致你的视图被重建。
  • 某些生命周期回调的触发:比如.onAppear、.task,如果视图在这些回调执行前被重建,新的VM实例会覆盖旧的,你之前的操作就白做了。

什么时候必须用@State包裹@Observable的VM?

一句话总结:当你需要VM的状态在视图的多次重建之间被保留时,就必须用@State。具体到业务场景里:

  1. 视图需要跨生命周期保留状态:比如你的ProductDetailsView里的错误提示,需要在视图出现后一直存在,不能因为视图重建就消失,这时候@State是必须的。
  2. 在生命周期回调里修改VM状态:只要你在.onAppear、.task、.onChange这些回调里修改VM的属性,几乎都得用@State——不然视图一重建,回调里的操作就等于在新的VM上执行,旧状态直接丢了。
  3. 视图是动态生成的:比如在List、ForEach里的子视图,当列表数据源变化时,SwiftUI可能会重建部分子视图,这时候必须用@State来保留每个子视图的VM状态。
  4. 视图可能被导航/布局操作反复创建销毁:比如需要频繁进入退出的详情页、编辑页,这类视图的VM一定要用@State,不然每次进入都得重新初始化,状态全丢。

最后再补个小提醒

别被第一个例子的“正常工作”误导——那只是刚好那个视图没被重建而已。实际开发里,几乎所有承载业务逻辑的视图,都有可能因为各种原因被SwiftUI重建,所以我的个人习惯是:只要是@Observable的视图模型,一律用@State存储,省得哪天突然遇到UI抽风的情况,再回头排查就麻烦了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:44:33