SwiftUI - возможно ли, чтобы didSet срабатывал при изменении структуры @Published?

Я только что обновился до XCode 11.4, и часть моего кода перестала работать. у меня есть немного@Published структурные переменные в ObservableObject. Раньше, когда я обновлял свойства в структуре,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 метод издателя?

2 ответа

Решение

Наблюдатель за недвижимостью наблюдает за собственностью. Проблема связана с новым синтаксисом Swift, связанным с оболочками свойств. В вашем случае вы пытаетесь наблюдать, изменилось ли значение опубликованного (который является структурой, определяющей специализированную оболочку свойств), а не значение обернутого свойства.

Если вам нужно отслеживать значения влево или вправо в 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()
    }
}

Или воспользуйтесь преимуществом того, что опубликованное прогнозируемое значение - Издатель, и примените модификатор next к любому представлению.

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

Вы можете подписаться на @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)
    }
}
Другие вопросы по тегам