ObervableObject инициируется несколько раз и не обновляет мое представление - PullRequest
0 голосов
/ 18 февраля 2020

У меня есть такая структура

contentView {
    navigationView{
     foreach {
        NavigationLink(ViewA(id: id))
     }
    }
}

///, где View A содержит триггер запроса в представлении Появляются

struct ViewA: View {

    @State var filterString: String = ""

    var id: String!
    @ObservedObject var model: ListObj = ListObj()

    init(id: String) {
        self.id = id
    }

    var body: some View {
        VStack {
            SearchBarView(searchText: $filterString)
            List {
                ForEach(model.items.filter({ filterString.isEmpty || $0.id.contains(filterString) || $0.name.contains(filterString)  }), id: \.id) { item in
                    NavigationLink(destination: ViewB(id: item.id)) {
                        VStack {
                            Text("\(item.name) ")
                        }
                    }
                }
            }

        }
        .onAppear {
            self.model.getListObj(id: self.id) //api request, fill data and call objectWillChange.send()
        }

    }

}

}

ViewB имеет тот же код, что и ViewB, идентификатор получения , сохранить и запросить API для сбора данных.

Но список viewB не обновляется. я также заметил, что

@ObservedObject var model: model = model()

от viewB был отлажен для многократной

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

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

1 Ответ

2 голосов
/ 19 февраля 2020

Здесь есть две проблемы:

  1. SwiftUI использует типы значений, которые инициализируются снова и снова при каждом проходе через body.
  2. Относится к # 1, NavigationLink не ленивый.

# 1

Новый ListObj создается каждый раз, когда вы звоните ViewA.init(...). ObservedObject не работает так же, как @State, где SwiftUI тщательно следит за ним на протяжении всего жизненного цикла на экране. SwiftUI предполагает, что окончательное владение @ObservedObject существует на некотором уровне выше View, в котором он используется.

Другими словами, вы почти всегда должны избегать таких вещей, как @ObservedObject var myObject = MyObservableObject().

(Обратите внимание, что даже если вы сделали @State var model = ListObj(), он будет создаваться каждый раз. Но так как @State SwiftUI заменит новый экземпляр оригиналом до вызова body.)

# 2

Кроме того, NavigationLink не ленив. Каждый раз, когда вы создаете экземпляр NavigationLink, вы передаете только что созданный экземпляр ViewA, который создает экземпляр вашего ListObj.

Итак, для начала одну вещь, которую вы можете сделать, это сделать LazyView, чтобы отложить инстанцирование до тех пор, пока NavigationLink.destination.body фактически не будет вызвано:

// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
    var content: () -> Content
    var body: some View {
        self.content()
    }
}

Теперь вы можете сделать NavigationLink(destination: LazyView { ViewA() }) и инстанцирование ViewA будет отложено до фактического отображения destination.

Простое использование LazyView исправит вашу текущую проблему , пока это вид сверху в иерархии , например это когда вы набираете sh его в NavigationView или если вы его представляете.

Тем не менее, именно здесь приходит комментарий @ user3441734. Что вам действительно нужно сделать, так это сохранить право собственности на model где-то за пределами вашего View из-за того, что было объяснено в # 1.

...