修改@Published结构体嵌套属性时如何触发didSet方法?
嘿,这个问题我之前也碰到过!其实这不是Swift的设计变更,而是Xcode 11.4修复了之前的行为异常,回到了@Published的正确逻辑上:
@Published修饰的值类型属性,只有在整个属性被重新赋值的时候,才会触发didSet方法,同时向SwiftUI发送数据变更通知。你的PaddingRect是结构体(值类型),直接修改它的内部属性(比如paddingRect.left += 20)并没有对paddingRect本身执行赋值操作,所以didSet不会被触发,SwiftUI也不会收到更新通知(不过结构体的属性确实已经被修改了,只是视图和didSet没响应而已)。
下面给你几个可行的解决方案:
方案1:手动触发更新+抽离业务逻辑
如果你想保留结构体的设计,可以在修改内部属性后,手动调用objectWillChange.send()通知SwiftUI更新视图,同时把didSet里的逻辑抽成独立方法手动调用:
final class SomeStore : ObservableObject { @Published var someOtherValue: String = "Waiting for didSet" @Published var paddingRect:PaddingRect = PaddingRect() { didSet { updateSomeOtherValue() } } // 把didSet里的逻辑抽出来,方便手动调用 func updateSomeOtherValue() { someOtherValue = "didSet fired" } } // 修改Button的action逻辑: Button(action: { self.store.paddingRect.left += 20 self.store.objectWillChange.send() // 手动通知视图更新 self.store.updateSomeOtherValue() // 触发原本didSet里的逻辑 }) { Text("Padding left +20") }
方案2:将结构体改为ObservableObject类
把PaddingRect改成遵循ObservableObject的引用类型(class),内部属性用@Published修饰,然后在SomeStore中监听它的变更:
// 把结构体改成class并遵循ObservableObject class PaddingRect: ObservableObject { @Published var left: CGFloat = 20 @Published var right: CGFloat = 20 } final class SomeStore : ObservableObject { @Published var someOtherValue: String = "Waiting for didSet" @Published var paddingRect: PaddingRect = PaddingRect() init() { // 监听paddingRect的内部属性变化,触发我们需要的逻辑 _ = paddingRect.objectWillChange.sink { [weak self] _ in self?.someOtherValue = "didSet fired" } } } // 视图里的Button不需要额外修改,直接改内部属性即可: Button(action: { self.store.paddingRect.left += 20 }) { Text("Padding left +20") }
这种方式下,修改paddingRect.left会触发PaddingRect内部的@Published发送通知,SwiftUI会自动更新视图,同时我们通过sink监听这个变更来更新someOtherValue。
方案3:重新赋值整个结构体(你已经发现的方法)
每次修改结构体属性时,创建一个新的结构体实例赋值给paddingRect,这样会触发didSet和@Published的变更通知:
Button(action: { self.store.paddingRect = PaddingRect( left: self.store.paddingRect.left + 20, right: self.store.paddingRect.right ) }) { Text("Padding left +20") }
这个方法最直接,但如果结构体的属性很多,写起来会比较繁琐,适合简单结构体的场景。
内容的提问来源于stack exchange,提问作者codewithfeeling

