Is it correct to expect internal updates of a SwiftUI DynamicProperty property wrapper to trigger a view update?

后端 未结 2 923
萌比男神i
萌比男神i 2020-12-10 13:58

I\'m attempting to create a custom property wrapper supported by SwiftUI, meaning that changes to the corresponding properties values would cause an update to the SwiftUI vi

相关标签:
2条回答
  • 2020-12-10 14:14

    Ok... here is alternate approach to get similar thing... but as struct only DynamicProperty wrapped around @State (to force view refresh).

    It is simple wrapper but gives possibility to incapsulate any custom calculations with following view refresh... and as said using value-only types.

    Here is demo (tested with Xcode 11.2 / iOS 13.2):

    DynamicProperty as wrapper on @State

    Here is code:

    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()
        }
    }
    
    0 讨论(0)
  • 2020-12-10 14:15

    Yes this is correct, here is an example:

    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
            }
        }
    }
    

    When a View using @Foo is recreated, SwiftUI passes the Foo struct the same object as last time, so has the same counter value. When setting the foo var, this is set on the ObservableObject's @Published which SwiftUI detects as a change and causes the body to be recomputed.

    Try it out!

    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()
        }
    }
    

    When the second button is tapped the counter stored in Foo is updated. When first button is tapped, ContentView's body is called and ContentView2() is recreated but keeps the same counter as last time. Now SomeObservedObject can be a NSObject and implement a delegate protocol for example.

    0 讨论(0)
提交回复
热议问题