SwiftUI中点击按钮后ProgressView不显示的问题排查
问题原因
核心问题是嵌套的ObservableObject属性变化无法触发外层ObservableObject的更新通知:
- User类中用
@Published修饰的credentials是Login类型(本身也是ObservableObject),但@Published仅会在credentials这个对象的引用被替换时(比如重新赋值user.credentials = Login())发送更新通知。 - 当修改
credentials内部的showLoginProgress时,Login自身的objectWillChange会触发,但User的objectWillChange不会同步触发,导致依赖User的LoginView无法感知到内部属性变化,因此不会刷新显示ProgressView。
解决方案
以下三种修复方式可根据实际场景选择:
方案1:将Login改为值类型(struct)
这是SwiftUI的推荐实践,值类型的属性变化会自动触发@Published的更新:
// 把Login从class改为struct,无需继承ObservableObject struct Login { var showLoginProgress: Bool = false } class User: ObservableObject { @Published var credentials = Login() }
此时修改user.credentials.showLoginProgress = true时,由于struct是值类型,credentials会被视为整体变化,User的@Published会发送通知,LoginView就能正常刷新。
方案2:让User监听Login的变化
若必须保留Login为class(比如有其他业务需求),可让User手动订阅Login的objectWillChange事件,转发给自己的objectWillChange:
import Combine class User: ObservableObject { var credentials = Login() private var cancellables = Set<AnyCancellable>() init() { // 订阅Login的变化,转发给User的objectWillChange credentials.objectWillChange .sink(receiveValue: { [weak self] _ in self?.objectWillChange.send() }) .store(in: &cancellables) } } class Login: ObservableObject { @Published var showLoginProgress: Bool = false }
这样当Login内部属性变化时,User会同步发送更新通知,触发View刷新。
方案3:在User中直接转发属性
如果仅需监听showLoginProgress这一个属性,可在User中直接定义对应的@Published属性,转发Login的属性:
import Combine class User: ObservableObject { var credentials = Login() @Published var showLoginProgress: Bool { get { credentials.showLoginProgress } set { credentials.showLoginProgress = newValue } } private var cancellables = Set<AnyCancellable>() init() { // 监听Login的属性变化,同步到User的属性 credentials.$showLoginProgress .assign(to: \.showLoginProgress, on: self) .store(in: &cancellables) } }
之后在LoginView中直接使用user.showLoginProgress即可:
if user.showLoginProgress { ProgressView() }
内容的提问来源于stack exchange,提问作者Nazar
相关产品推荐
相关产品推荐

