简体   繁体   English

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

[英]SwiftUI - is it possible to get didSet to fire when changing a @Published struct?

I have just updated to XCode 11.4 and some of my code has stopped working.我刚刚更新到 XCode 11.4,我的一些代码已经停止工作。 I have some @Published struct variables in an ObservableObject .我在ObservableObject有一些@Published结构变量。 Previously, when I updated properties on the struct, the didSet method would fire on the published property, but that's not the case anymore.以前,当我更新结构上的属性时, didSet方法会在已发布的属性上触发,但现在情况不再如此。 Is it possible that this behaviour has changed by design in the latest update to Swift?在 Swift 的最新更新中,这种行为是否有可能被设计改变了?

Here's a trivial example:这是一个简单的例子:


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()
    }
}

The property updates, but didSet does not fire.属性更新,但didSet不会触发。

Is it possible to get nested properties of a struct to trigger the didSet method of the publisher?是否可以获取结构的嵌套属性来触发发布者的didSet方法?

You can subscribe to the @Published value stream in the class itself.您可以订阅类本身中的@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)
    }
}

The property observer observes the property.财产观察者观察财产。 The trouble goes from new Swift syntax related to property wrappers.问题来自与属性包装器相关的新 Swift 语法。 In your case you try to observe if value of Published (which is a struct defining the specialized property wrapper) did change, not the value of the wrapped property.在您的情况下,您尝试观察 Published (这是一个定义专用属性包装器的结构)的值是否确实发生了变化,而不是包装属性的值。

If you need to monitor left or right values in PaddingRect, simply observe this values directly.如果您需要监视 PaddingRect 中的左值或右值,只需直接观察此值即可。

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()
    }
}

在此处输入图片说明

Or take the advantage that Published projected value is Publisher and aply next modifier to any View或者利用 Published 预测值是 Publisher并将下一个修饰符应用于任何视图

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM