期望SwiftUI DynamicProperty属性包装的内部更新触发视图更新是否正确?
我正在尝试创建SwiftUI支持的自定义属性包装,这意味着对相应属性值的更改将导致对SwiftUI视图的更新。以下是我所拥有内容的简化版本:期望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
@propertyWrapper
public struct Foo: DynamicProperty {
@ObservedObject var observed: SomeObservedObject
public var wrappedValue: [SomeValue] {
return observed.value
}
}
我发现,即使我的ObservedObject
包含在我的自定义属性包装中,SwiftUI仍然可以捕获对SomeObservedObject
的更改,只要:
- 我的属性包装器是一个结构
- 我的属性包装器符合
DynamicProperty
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所做的那样,动态属性
本身并不强制执行任何更新。不清楚本主题的期望是什么。。。回答最后一个问题?如果有人回答“是的,当然,你可以期待”,你真的会相信吗