SwiftUI - распространение уведомлений об изменениях через вложенные ссылочные типы - PullRequest
3 голосов
/ 07 февраля 2020

Я бы хотел расширить поведение ObservableObject в SwiftUI для вложенных классов, и я ищу правильный способ сделать это. Это можно сделать «вручную» с помощью Combine, но я думаю, что есть более чистый способ сделать это с помощью SwiftUI, и я надеюсь, что вы можете указать мне правильное направление. Вот что я имею в виду…

Ниже приведено типичное приложение ObservableObject для динамического реагирования View на изменения ссылочного типа. Нажатие на кнопку переключает значение showText, что приводит к тому, что текст появляется / исчезает на экране:

import SwiftUI

class MyClass: ObservableObject {
    @Published var showText = false
}


struct ContentView: View {

    @ObservedObject var instance = MyClass()

    var body: some View {
        VStack(spacing: 10) {
            Button(action: {
                print(self.instance.showText)
                self.instance.showText.toggle()
            }) {
                Text("BUTTON").bold().padding()
                    .foregroundColor(.white)
                    .background(Color.red)
            }
            if instance.showText {
                Text("Hello, World!")
            }
        }
    }
}

Это прекрасно работает.

Но как насчет модификации ниже, где класс содержащий showText является InnerClass, сам содержится в OuterClass? Кнопка отлично переключает showText, но уведомление об изменении значения больше не распространяется через экземпляр OuterClass в представление, поэтому представление больше не отображает текст вообще.

import SwiftUI

class OuterClass: ObservableObject {
    @Published var innerInstance = InnerClass()
}

class InnerClass: ObservableObject {
    @Published var showText = false
}

struct ContentView: View {

    @ObservedObject var outerInstance = OuterClass()

    var body: some View {
        VStack(spacing: 10) {
            Button(action: {
                self.outerInstance.innerInstance.showText.toggle()
            }) {
                Text("BUTTON").bold().padding()
                    .foregroundColor(.white)
                    .background(Color.red)
            }
            if outerInstance.innerInstance.showText {
                Text("Hello, World!")
            }
        }
    }
}

Что это элегантное решение для этого?

Ответы [ 2 ]

2 голосов
/ 08 февраля 2020

Явно позвоните издателю, и вы получите уведомление:

struct ContentView: View {   
  @ObservedObject var outerInstance = OuterClass()
  var body: some View {
    VStack(spacing: 10) {
      Button(action: {
        self.outerInstance.innerInstance.showText.toggle()
        // Call the publisher
        self.outerInstance.objectWillChange.send()
      }) {
        Text("BUTTON").bold().padding()
          .foregroundColor(.white)
          .background(Color.red)
      }
      if outerInstance.innerInstance.showText {
        Text("Hello, World!")
      }
    }
  }
}
1 голос
/ 08 февраля 2020

это можно сделать в вашей модели

import Combine // required for AnyCancelable

class OuterClass: ObservableObject {
    private let _inner: InnerClass
    var innerInstance: InnerClass {
        return _inner
    }
    var store = Set<AnyCancellable>()
    init(_ inner: InnerClass) {
        _inner = inner
        inner.$showText.sink { [weak self] _ in
            self?.objectWillChange.send()
        }.store(in: &store)
    }
}

и как использовать в вашем примере

import SwiftUI
import Combine

class OuterClass: ObservableObject {
    private let _inner: InnerClass
    var innerInstance: InnerClass {
        return _inner
    }
    var store = Set<AnyCancellable>()
    init(_ inner: InnerClass) {
        _inner = inner
        inner.$showText.sink { [weak self] _ in
            self?.objectWillChange.send()
        }.store(in: &store)
    }
}

class InnerClass: ObservableObject {
    @Published var showText = false
}

let inner = InnerClass()
let outer = OuterClass(inner)

struct ContentView: View {

    @ObservedObject var outerInstance = outer

    var body: some View {
        VStack(spacing: 10) {
            Button(action: {
                self.outerInstance.innerInstance.showText.toggle()
            }) {
                Text("BUTTON").bold().padding()
                    .foregroundColor(.white)
                    .background(Color.red)
            }
            if outerInstance.innerInstance.showText {
                Text("Hello, World!")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

, если вам нравится , чтобы наблюдать любые изменения в вашем внутреннем объекте , просто сделай это!

class OuterClass: ObservableObject {
    private let _inner: InnerClass
    var innerInstance: InnerClass {
        return _inner
    }
    var store = Set<AnyCancellable>()
    init(_ inner: InnerClass) {
        _inner = inner
        inner.objectWillChange.sink { [weak self] _ in
            self?.objectWillChange.send()
        }.store(in: &store)
    }
}

ОБНОВЛЕНИЕ: на основе обсуждения ниже

class OuterClass: Combine.ObservableObject {
    private let _inner: InnerClass
    var innerInstance: InnerClass {
        return _inner
    }
    var store = Set<AnyCancellable>()
    init(_ inner: InnerClass) {
        _inner = inner
        inner.objectWillChange.sink { [weak self] _ in
            self?.objectWillChange.send()
        }.store(in: &store)
    }
}
...