SwiftUI - распространение уведомлений об изменениях через вложенные ссылочные типы
Я хотел бы расширить поведение 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
экземпляр в View, поэтому View больше не отображает текст вообще.
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 ответа
Это может быть сделано в вашей модели
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)
}
}
Позвоните издателю явно, и вы получите уведомление:
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!")
}
}
}
}