如何实现自定义 属性 包装器,它将发布 SwiftUI 的更改以重新呈现它的视图
How to implement a custom property wrapper which would publish the changes for SwiftUI to re-render it's view
尝试实现自定义 属性 包装器,该包装器也将以与 @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())
}
}
但是,我不喜欢需要为 MySettings
class 中的每个 属性 调用 objectWillChange.send()
。
@Published
包装器运行良好,所以我尝试将其作为 @MyWrapper
的一部分实现,但我没有成功。
我发现的一个很好的灵感是 https://github.com/broadwaylamb/OpenCombine,但即使尝试使用那里的代码我也失败了。
在努力实施时,
我意识到,为了让 @MyWrapper
工作,我需要准确理解 @EnvironmentObject
和 @ObservedObject
如何订阅 @Published
.
的变化
如有任何帮助,我们将不胜感激。
在 https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type 实施之前,我想出了以下解决方案。
通常,我使用反射将 MySettings
的 objectWillChange
引用传递给所有用 @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())
}
}
尝试实现自定义 属性 包装器,该包装器也将以与 @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())
}
}
但是,我不喜欢需要为 MySettings
class 中的每个 属性 调用 objectWillChange.send()
。
@Published
包装器运行良好,所以我尝试将其作为 @MyWrapper
的一部分实现,但我没有成功。
我发现的一个很好的灵感是 https://github.com/broadwaylamb/OpenCombine,但即使尝试使用那里的代码我也失败了。
在努力实施时,
我意识到,为了让 @MyWrapper
工作,我需要准确理解 @EnvironmentObject
和 @ObservedObject
如何订阅 @Published
.
如有任何帮助,我们将不胜感激。
在 https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type 实施之前,我想出了以下解决方案。
通常,我使用反射将 MySettings
的 objectWillChange
引用传递给所有用 @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())
}
}