Правильно ли ожидать внутренних обновлений оболочки свойства SwiftUI DynamicProperty для запуска обновления представления?

10

Я пытаюсь создать пользовательскую оболочку свойства, поддерживаемую SwiftUI, то есть изменение соответствующих значений свойств приведет к обновлению представления SwiftUI. Вот упрощенная версия того, что у меня есть:

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

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

Я вижу, что даже если my ObservedObjectсодержится внутри моей оболочки пользовательских свойств, SwiftUI по-прежнему фиксирует изменения до SomeObservedObjectтех пор, пока:

  • Моя оболочка свойства является структурой
  • Моя оболочка соответствует DynamicProperty

К сожалению, документы довольно скудны, и мне трудно сказать, удастся ли это только удаче с текущей реализацией SwiftUI.

Документы о DynamicProperty (в Xcode, а не в сети), по-видимому, указывают на то, что такое свойство является свойством, которое изменяется извне, вызывая перерисовку представления, но нет никакой гарантии относительно того, что происходит, когда вы настраиваете свои собственные типы для этого протокола.

Могу ли я ожидать, что это продолжит работать в будущих выпусках SwiftUI?

Тревор
источник
4
Непонятно, чего ожидает эта тема ... ответьте на последний вопрос? Вы действительно поверите, если кто-то ответит «да, конечно, вы можете ожидать»? ))
Аспери

Ответы:

6

Хорошо ... вот альтернативный подход, чтобы получить похожую вещь ... но так как структура только DynamicPropertyобернута@State (чтобы заставить представление обновить).

Это простая оболочка, но она дает возможность инкапсулировать любые пользовательские вычисления с последующим обновлением представления ... и, как сказано, используя типы только для значений.

Вот демо (протестировано с Xcode 11.2 / iOS 13.2):

DynamicProperty в качестве оболочки на @State

Вот код:

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
источник