SwiftUI - 更改 @Published 结构时是不是可以触发 didSet?

Posted

技术标签:

【中文标题】SwiftUI - 更改 @Published 结构时是不是可以触发 didSet?【英文标题】:SwiftUI - is it possible to get didSet to fire when changing a @Published struct?SwiftUI - 更改 @Published 结构时是否可以触发 didSet? 【发布时间】:2020-07-10 22:28:47 【问题描述】:

我刚刚更新到 XCode 11.4,我的一些代码已经停止工作。我在ObservableObject 中有一些@Published 结构变量。以前,当我更新结构上的属性时,didSet 方法会在发布的属性上触发,但现在情况不再如此。这种行为是否有可能在 Swift 的最新更新中被设计改变了?

这是一个简单的例子:


import SwiftUI

struct PaddingRect 
  var left: CGFloat = 20
  var right: CGFloat = 20


final class SomeStore : ObservableObject 
  @Published var someOtherValue: String = "Waiting for didSet"

  @Published var paddingRect:PaddingRect = PaddingRect() 
    didSet 
      someOtherValue = "didSet fired"
    
  


struct ObserverIssue: View 
  @ObservedObject var store = SomeStore()

  var body: some View 
    VStack 
      Spacer()

      Rectangle()
        .fill(Color.yellow)
        .padding(.leading, store.paddingRect.left)
        .padding(.trailing, store.paddingRect.right)
        .frame(height: 100)

      Text(store.someOtherValue)

      HStack 
        Button(action: 
          // This doesn't call didSet
          self.store.paddingRect.left += 20

          // This does call didSet, ie. setting the whole thing
//          self.store.paddingRect = PaddingRect(
//            left: self.store.paddingRect.left + 20,
//            right: self.store.paddingRect.right
//          )

        ) 
          Text("Padding left +20")
        

        Button(action: 
          self.store.paddingRect.right += 20
        ) 
          Text("Padding right +20")
        
      

      Spacer()
    
  


struct ObserverIssue_Previews: PreviewProvider 
    static var previews: some View 
        ObserverIssue()
    


属性更新,但didSet 没有触发。

是否可以获取结构体的嵌套属性来触发发布者的didSet 方法?

【问题讨论】:

奇怪的是,在我升级之前它一直在我的代码中工作,仅此而已。当您看到我的实际问题是“是否可以获取结构的嵌套属性以触发发布者的 didSet 方法”时,我将更改问题的标题 @Asperi:我遇到了同样的问题......并且在我更新之前调用了 didSet - 这是真的。也许之前是一个错误......但它有效;)我喜欢这个功能,我错过了...... 看看这个:***.com/a/59391476/8457280。 -> 然后它再次工作;) 【参考方案1】:

您可以在课程本身中订阅@Published 价值流。

final class SomeStore: ObservableObject 
    @Published var someOtherValue: String = "Waiting for didSet"
    @Published var paddingRect: PaddingRect = PaddingRect()
    private var subscribers: Set<AnyCancellable> = []
    
    init() 
        $paddingRect.sink  paddingRect in
            print(paddingRect) // ?
        .store(in: &subscribers)
    

【讨论】:

记住Published.Publisher(通过$paddingRect访问)是实现willSet,而不是didSet @DávidPásztor 是的,这种预期差异可能会导致令人讨厌的细微错误:forums.swift.org/t/is-this-a-bug-in-published/31292【参考方案2】:

属性观察者观察属性。麻烦来自与属性包装器相关的新 Swift 语法。在您的情况下,您尝试观察 Published 的值(这是一个定义专用属性包装器的结构)是否发生了变化,而不是包装属性的值。

如果需要监控PaddingRect中的left或right值,直接观察这个值即可。

import SwiftUI


struct PaddingRect 
    var left: CGFloat = 20 
        didSet 
            print("left padding change from:", oldValue, "to:", left)
        
    
    var right: CGFloat = 20 
        didSet 
            print("right padding change from:", oldValue, "to:", right)
        
    


final class SomeStore : ObservableObject 
    @Published var someOtherValue: String = "Waiting for didSet"
    @Published var paddingRect:PaddingRect = PaddingRect()


struct ContentView: View 
    @ObservedObject var store = SomeStore()

    var body: some View 
        VStack 
            Spacer()

            Rectangle()
                .fill(Color.yellow)
                .padding(.leading, store.paddingRect.left)
                .padding(.trailing, store.paddingRect.right)
                .frame(height: 100)

            Text(store.someOtherValue)

            HStack 
                Button(action: 
                    // This doesn't call didSet
                    self.store.paddingRect.left += 20

                    // This does call didSet, ie. setting the whole thing
                    self.store.paddingRect = PaddingRect(
                        left: self.store.paddingRect.left + 20,
                        right: self.store.paddingRect.right
                    )

                ) 
                    Text("Padding left +20")
                

                Button(action: 
                    self.store.paddingRect.right += 20
                ) 
                    Text("Padding right +20")
                
            

            Spacer()
        
    


struct ContentView_Preview: PreviewProvider 
    static var previews: some View 
        ContentView()
    

利用已发布的预计值是发布者并将下一个修饰符应用于任何视图

.onReceive(store.$paddingRect)  (p) in
            print(p)
        

【讨论】:

以上是关于SwiftUI - 更改 @Published 结构时是不是可以触发 didSet?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:“@Published”属性更改时未刷新“带有组的动态列表”

当视图模型@Published 更改时,SwiftUI 列表因“NSRangeException”而崩溃

当我通过鼠标单击更改 SwiftUI 列表的选择时,@Published 属性的 didSet 被调用了两次

当我的@Published NSManagedObjects 数组更改时,我的 SwiftUI 列表不会更新

在 SwiftUI 中,如何对“视图”的“@Published vars”*outside* 上的更改做出反应

SwiftUI 如何将@Published 分配给另一个@Published