Как реализовать настраиваемую оболочку свойств, которая будет публиковать изменения для SwiftUI, чтобы повторно отобразить его представление
Попытка реализовать настраиваемую оболочку свойств, которая также публиковала бы свои изменения таким же образом @Publish
делает. Например, разрешить моему SwiftUI получать изменения в моей собственности с помощью моей пользовательской оболочки.
Рабочий код у меня есть:
import SwiftUI
@propertyWrapper
struct MyWrapper<Value> {
var value: Value
init(wrappedValue: Value) { value = wrappedValue }
var wrappedValue: Value {
get { value }
set { value = newValue }
}
}
class MySettings: ObservableObject {
@MyWrapper
public var interval: Double = 50 {
willSet { objectWillChange.send() }
}
}
struct MyView: View {
@EnvironmentObject var settings: MySettings
var body: some View {
VStack() {
Text("\(settings.interval, specifier: "%.0f")").font(.title)
Slider(value: $settings.interval, in: 0...100, step: 10)
}
}
}
struct MyView_Previews: PreviewProvider {
static var previews: some View {
MyView().environmentObject(MySettings())
}
}
Однако мне не нравится необходимость звонить objectWillChange.send()
для каждой собственности в MySettings
учебный класс.
В @Published
оболочка работает хорошо, поэтому я попытался реализовать ее как часть @MyWrapper
, но мне это не удалось.
Хорошим вдохновением, которое я нашел, был https://github.com/broadwaylamb/OpenCombine, но я потерпел неудачу даже при попытке использовать код оттуда.
Когда боролся с реализацией, я понял, что для получения @MyWrapper
мне нужно точно понимать, как @EnvironmentObject
а также @ObservedObject
подписаться на изменения @Published
.
Любая помощь будет оценена.
1 ответ
Пока не будет реализован https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md, я пришел с решением ниже.
Как правило, я сдаю objectWillChange
ссылка на MySettings
ко всем свойствам, помеченным @MyWrapper
используя отражение.
import Cocoa
import Combine
import SwiftUI
protocol PublishedWrapper: class {
var objectWillChange: ObservableObjectPublisher? { get set }
}
@propertyWrapper
class MyWrapper<Value>: PublishedWrapper {
var value: Value
weak var objectWillChange: ObservableObjectPublisher?
init(wrappedValue: Value) { value = wrappedValue }
var wrappedValue: Value {
get { value }
set {
value = newValue
objectWillChange?.send()
}
}
}
class MySettings: ObservableObject {
@MyWrapper
public var interval1: Double = 10
@MyWrapper
public var interval2: Double = 20
/// Pass our `ObservableObjectPublisher` to the property wrappers so that they can announce changes
init() {
let mirror = Mirror(reflecting: self)
mirror.children.forEach { child in
if let observedProperty = child.value as? PublishedWrapper {
observedProperty.objectWillChange = self.objectWillChange
}
}
}
}
struct MyView: View {
@EnvironmentObject
private var settings: MySettings
var body: some View {
VStack() {
Text("\(settings.interval1, specifier: "%.0f")").font(.title)
Slider(value: $settings.interval1, in: 0...100, step: 10)
Text("\(settings.interval2, specifier: "%.0f")").font(.title)
Slider(value: $settings.interval2, in: 0...100, step: 10)
}
}
}
struct MyView_Previews: PreviewProvider {
static var previews: some View {
MyView().environmentObject(MySettings())
}
}