期望SwiftUI DynamicProperty属性包装的内部更新触发视图更新是否正确?

期望SwiftUI DynamicProperty属性包装的内部更新触发视图更新是否正确?,swift,swiftui,property-wrapper,Swift,Swiftui,Property Wrapper,我正在尝试创建SwiftUI支持的自定义属性包装,这意味着对相应属性值的更改将导致对SwiftUI视图的更新。以下是我所拥有内容的简化版本: @propertyWrapper public struct Foo: DynamicProperty { @ObservedObject var observed: SomeObservedObject public var wrappedValue: [SomeValue] { return observed.valu

我正在尝试创建SwiftUI支持的自定义属性包装,这意味着对相应属性值的更改将导致对SwiftUI视图的更新。以下是我所拥有内容的简化版本:

@propertyWrapper
public struct Foo: DynamicProperty {
    @ObservedObject var observed: SomeObservedObject

    public var wrappedValue: [SomeValue] {
        return observed.value
    }
}
我发现,即使我的
ObservedObject
包含在我的自定义属性包装中,SwiftUI仍然可以捕获对
SomeObservedObject
的更改,只要:

  • 我的属性包装器是一个结构
  • 我的属性包装器符合
    DynamicProperty
不幸的是,这些文档很少,我很难判断这是否仅仅是因为当前的SwiftUI实现运气好

DynamicProperty
(在Xcode中,非在线)的文档似乎表明这样一个属性是从外部更改的属性,导致视图重新绘制,但无法保证当您将自己的类型与此协议一致时会发生什么


我能指望它在未来的SwiftUI版本中继续工作吗?

好的。。。这里有另一种方法来得到类似的东西。。。但仅作为struct
DynamicProperty
围绕
@State
(强制视图刷新)

它是一个简单的包装器,但可以通过以下视图刷新来封装任何自定义计算。。。如前所述,只使用值类型

以下是演示(使用Xcode 11.2/iOS 13.2进行测试):

以下是代码:

import SwiftUI

@propertyWrapper
struct Refreshing<Value> : DynamicProperty {
    let storage: State<Value>

    init(wrappedValue value: Value) {
        self.storage = State<Value>(initialValue: value)
    }

    public var wrappedValue: Value {
        get { storage.wrappedValue }

        nonmutating set { self.process(newValue) }
    }

    public var projectedValue: Binding<Value> {
        storage.projectedValue
    }

    private func process(_ value: Value) {
        // do some something here or in background queue
        DispatchQueue.main.async {
            self.storage.wrappedValue = value
        }
    }

}


struct TestPropertyWrapper: View {

    @Refreshing var counter: Int = 1
    var body: some View {
        VStack {
            Text("Value: \(counter)")
            Divider()
            Button("Increase") {
                self.counter += 1
            }
        }
    }
}

struct TestPropertyWrapper_Previews: PreviewProvider {
    static var previews: some View {
        TestPropertyWrapper()
    }
}
导入快捷界面
@房地产经纪人
结构刷新:DynamicProperty{
let存储:状态
init(wrappedValue值:value){
self.storage=State(初始值:value)
}
公共var wrappedValue:Value{
获取{storage.wrappedValue}
非交换集{self.process(newValue)}
}
公共var projectedValue:绑定{
存储.projectedValue
}
私有func进程(value:value){
//在这里或后台队列中执行一些操作
DispatchQueue.main.async{
self.storage.wrappedValue=值
}
}
}
结构TestPropertyRapper:视图{
@刷新变量计数器:Int=1
var body:一些观点{
VStack{
文本(“值:\(计数器)”)
分隔器()
按钮(“增加”){
self.counter+=1
}
}
}
}
结构TestPropertyRapper\u预览:PreviewProvider{
静态var预览:一些视图{
TestPropertyRapper()
}
}

是,这是正确的,下面是一个示例:

class SomeObservedObject : ObservableObject {
    @Published var counter = 0
}

@propertyWrapper struct Foo: DynamicProperty {

    @StateObject var object = SomeObservedObject()

    public var wrappedValue: Int {
        get {
            object.counter
        }
        nonmutating set {
            object.counter = newValue
        }
    }
}
当重新创建使用
@Foo
的视图时,SwiftUI会向Foo结构传递与上次相同的对象,因此具有相同的计数器值。当设置foo变量时,这是在SwiftUI检测为更改并导致主体重新计算的
ObservableObject
@Published
上设置的

试试看

struct ContentView: View {
    @State var counter = 0
    var body: some View {
        VStack {
            Text("\(counter) Hello, world!")
            Button("Increment") {
                counter = counter + 1
            }
            ContentView2()
        }
        .padding()
    }
}

struct ContentView2: View {
    @Foo var foo
    
    var body: some View {
        VStack {
            Text("\(foo) Hello, world!")
            Button("Increment") {
                foo = foo + 1
            }
        }
        .padding()
    }
}

点击第二个按钮时,存储在
Foo
中的计数器将更新。点击第一个按钮时,将调用
ContentView
的主体,并重新创建
ContentView2()
,但保留与上次相同的计数器。现在,
SomeObservedObject
可以是一个
NSObject
,并实现一个
委托
协议,例如。

对于标题中的问题,否。例如,以下是一些不起作用的代码:

class Storage {
    var value = 0
}

@propertyWrapper
struct MyState: DynamicProperty {
    var storage = Storage()

    var wrappedValue: Int {
        get { storage.value }

        nonmutating set {
            storage.value = newValue // This doesn't work
        }
    }
}

因此,显然您仍然需要将
状态
观察对象
等放入
动态属性
中以触发更新,就像Asperi所做的那样,
动态属性
本身并不强制执行任何更新。

不清楚本主题的期望是什么。。。回答最后一个问题?如果有人回答“是的,当然,你可以期待”,你真的会相信吗