Czy można oczekiwać wewnętrznych aktualizacji opakowania właściwości SwiftUI DynamicProperty, aby wywołać aktualizację widoku?

10

Próbuję utworzyć niestandardowe opakowanie właściwości obsługiwane przez SwiftUI, co oznacza, że ​​zmiany w odpowiednich wartościach właściwości spowodowałyby aktualizację widoku SwiftUI. Oto uproszczona wersja tego, co mam:

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

    public var wrappedValue: [SomeValue] {
        return observed.value
    }
}

Widzę, że nawet jeśli moja ObservedObjectzawartość jest zawarta w opakowaniu właściwości niestandardowej, SwiftUI nadal przechwytuje zmiany SomeObservedObjecttak długo, jak:

  • Moje opakowanie właściwości jest strukturą
  • Moje opakowanie właściwości jest zgodne z DynamicProperty

Niestety dokumenty są rzadkie i trudno mi powiedzieć, czy przy obecnej implementacji SwiftUI działa to tylko pechowo.

Dokumenty z DynamicProperty (w Xcode, nie online) wydają się wskazywać, że taka właściwość jest zmieniona z zewnątrz, powodując przerysowanie widoku, ale nie ma gwarancji, co się stanie, gdy dostosujesz własne typy do tego protokołu.

Czy mogę oczekiwać, że nadal będzie działać w przyszłych wersjach SwiftUI?

Trevör
źródło
4
Nie jest jasne, czego oczekuje ten temat ... odpowiedź na ostatnie pytanie? Czy naprawdę uwierzysz, jeśli ktoś odpowie „tak, jasne, możesz się spodziewać”? ))
Asperi

Odpowiedzi:

6

Ok ... tutaj jest alternatywne podejście do uzyskania podobnych rzeczy ... ale jako struktura jest tylko DynamicPropertyowinięta@State (aby wymusić odświeżenie widoku).

Jest to proste opakowanie, ale daje możliwość zawarcia dowolnych niestandardowych obliczeń przy kolejnym odświeżeniu widoku ... i, jak powiedziano, przy użyciu typów tylko wartościowych.

Oto wersja demonstracyjna (testowana z Xcode 11.2 / iOS 13.2):

DynamicProperty jako opakowanie na @State

Oto kod:

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()
    }
}
Asperi
źródło