Вычисляемые (NSObject) свойства в SwiftUI не обновляют представление

Итак, я хочу иметь Text, который изменяет свое содержимое в зависимости от содержимого моей модели CoreData. Для этого я использовал вычисляемое свойство в Xcode beta 4, но, похоже, оно больше не работает. Либо это ошибка, либо есть другая проблема, которую я не вижу?

Проблема, с которой я столкнулся, заключается в том, что мой View (и вычисляемое свойство), похоже, не обновляется, когда self.objectWillChange.send() вызывается в моем магазине.

Я также попытался «экспортировать» свой var в магазин и получить его оттуда с тем же результатом ...


РЕДАКТИРОВАТЬ: Я просто попробовал то же самое с другим классом, и это не сработало только с objectWillChange.send(), но только с @Published, однако даже это перестало работать, если класс унаследован от NSObject ...


Я только что узнал: с

struct Today: View {
    @EnvironmentObject var myStore: DateStore
    var hasPlans: Bool {
        guard let plans = myStore.getPlans() else { return false }
        return plans.isEmpty
    }

    var body: some View{
        VStack{
            Text(hasPlans ? "I have plans":"I have time today")
            Button(action: {
                self.myStore.addPlans(for: Date())
            }) {
                Text("I have plans")
            }
    }
}

class DateStore: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
    private var fetchedResultsController: NSFetchedResultsController<DateStore>
    //...
    public func addPlans(for date: Date){
        //{...}
        if let day = self.dates.first(where: { $0.date == date}){
            day.plans += 1
            saveChanges()
        }else{
            self.create(date: dayDate)
        }
    }

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        self.objectWillChange.send()
    }
}


Это очень упрощенная версия моей проблемы, и я знаю, что моя модель данных работает, потому что меняются значения и вызывается self.objectWillChange.send(), но мое представление по какой-то причине не обновляется ...


person thisIsTheFoxe    schedule 31.07.2019    source источник
comment
Вы учли изменения Beta 5? sarunw.com/posts/swiftui-changes-in-xcode- 11-beta-5   -  person matt    schedule 31.07.2019
comment
Да, я удалил все устаревшие методы и изменил все на ObservableObjects, как вы также видите в моем коде ... но я не нашел ничего о том, что изменилось в вычисленных свойствах или как обновляется View ...   -  person thisIsTheFoxe    schedule 31.07.2019
comment
Подтверждаю, что вижу ту же проблему.   -  person davidhelms    schedule 01.08.2019
comment
Я не вижу вашей реализации objectWillChange.   -  person matt    schedule 01.08.2019
comment
Я вижу аналогичную проблему, когда у меня есть подкласс класса, соответствующий @ObservableObject. см. здесь   -  person thiezn    schedule 22.08.2019


Ответы (3)


Я не вижу, что NSObject является источником проблемы. Проблема, похоже, в том, что вы не реализовали objectWillChange. Компилятор позволит вам уйти от этого, но в результате ваш objectWillChange ничего не делает.

Вот простой пример, который показывает, как настроить ObservableObject (то есть NSObject) с вычисляемым свойством, привязка которого работает:

class Thing : NSObject, ObservableObject {
    let objectWillChange = ObservableObjectPublisher()
    var computedProperty : Bool = true {
        willSet {
            self.objectWillChange.send()
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var thing : Thing
    var body: some View {
        VStack {
            Button(self.thing.computedProperty ? "Hello" : "Goodbye") {
                self.thing.computedProperty.toggle()
            }
            Toggle.init("", isOn: self.$thing.computedProperty).frame(width:0)
        }
    }
}

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

person matt    schedule 01.08.2019
comment
Но может я неправильно понял вопрос? Это про ваш hasPlans? Так вы ожидаете, что он волшебным образом пересчитается? В этом случае, да, вам понадобится собственный издатель и собственная подписка; только внутри body вы получаете автоматический пересчет при изменении привязки. - person matt; 01.08.2019

Экспериментируя с моим собственным кодом, выявляет аналогичную проблему.

Похоже, что волшебство @Publisher SwiftUI ломается, когда класс, принимающий ObservableObject, является подклассом NSObject.

Краткий ответ заключается в том, что если вы можете заставить его работать с @Published, когда он не является подклассом NSObject, тогда, когда вы сделаете его подклассом NSObject, замените @Published на

let objectWillChange = PassthroughSubject<Void, Never>()

Для этого вам нужно будет импортировать фреймворк Combine в файл класса.

Вот код из рабочего приложения, с которым я экспериментирую.

Вид:

import SwiftUI

struct ContentView: View {

    //Bind using @ObservedObject
    @ObservedObject var provider: Provider

    var body: some View {
        Text("\(self.provider.distance)")
    }
    ...
}

Класс:

import Combine

class Provider: NSObject, ObservableObject {

    //Instead of using @Published, use objectwillchange
    //@Published var distance: CLLocationDistance = 0.0
    let objectWillChange = PassthroughSubject<Void, Never>()
    var distance = 0.0
    ...

    func calculateDistance() {
        ...
        // publish the change
        self.objectWillChange.send()
        self.distance = newDistance
    }
}
person davidhelms    schedule 01.08.2019
comment
как вызывается calculateDistance ()? - person Duck; 24.02.2021

Одно из эффективных решений - просто создать new @State var вместо использования вычисляемого свойства. Однако, согласно WWDC говорит о SwiftUI, это кажется несколько неправильным, потому что мое «фактическое состояние» находится в моей модели данных, и, объявляя новое @State, мне нужно поддерживать их обоих в синхронизации, что противоречит шаблону «Единого источника истины». не так ли?

person thisIsTheFoxe    schedule 31.07.2019