J'ai une structure comme ça

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

///où la vue A contient un déclencheur de requête dans la vue Apparaît

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 a le même code que ViewB, Receive id, store et request api pour collecter des données.

Mais la liste viewB n'est pas actualisée. j'ai aussi remarqué les viewB

@ObservedObject var model: model = model()

A été instancié plusieurs fois

Débogage, j'ai trouvé chaque instantie navigationLink sa destination avant même qu'elle ne soit déclenchée. ce n'est généralement pas un problème,

Mais dans mon cas, j'ai l'impression que le modèle ViewB est instancié 2 fois, et mon onApear appelle le mauvais, raison pour laquelle self.objectWillChange.send() ne rafraîchit pas ma vue

4
Fernand 18 févr. 2020 à 13:32

2 réponses

Meilleure réponse

Il y a deux problèmes ici:

  1. SwiftUI utilise des types de valeur qui sont initialisés encore et encore à chaque passage par body.
  2. Lié à #1, NavigationLink n'est pas paresseux.

#1

Un nouveau ListObj est instancié à chaque fois que vous appelez ViewA.init(...). ObservedObject ne fonctionne pas de la même manière que @State où SwiftUI en garde une trace attentive pour vous tout au long du cycle de vie à l'écran. SwiftUI suppose que la propriété ultime d'un @ObservedObject existe à un certain niveau au-dessus du View dans lequel il est utilisé.

En d'autres termes, vous devriez presque toujours éviter des choses comme @ObservedObject var myObject = MyObservableObject().

(Remarque, même si vous le faisiez @State var model = ListObj(), il serait instancié à chaque fois. Mais parce que c'est @State, SwiftUI remplacera la nouvelle instance par l'originale avant que body ne soit appelé.)

# 2

En plus de cela, NavigationLink n'est pas paresseux. Chaque fois que vous instanciez ce NavigationLink, vous passez un ViewA nouvellement instancié, qui instancie votre ListObj.

Donc, pour commencer, une chose que vous pouvez faire est de faire un LazyView pour retarder l'instanciation jusqu'à ce que NavigationLink.destination.body soit réellement appelé :

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

Vous pouvez maintenant faire NavigationLink(destination: LazyView { ViewA() }) et l'instanciation de ViewA sera différée jusqu'à ce que destination soit réellement affiché.

Le simple fait d'utiliser LazyView résoudra votre problème actuel tant qu'il s'agit de la vue de dessus dans la hiérarchie, comme c'est le cas lorsque vous le poussez dans un NavigationView ou si vous le présentez.

Cependant, c'est là qu'intervient le commentaire de @ user3441734. Ce que vous devez vraiment faire, c'est conserver la propriété de model quelque part en dehors de votre View à cause de ce qui a été expliqué dans #1.

8
arsenius 19 févr. 2020 à 07:25

Si votre @ObservedObject est initialisé plusieurs fois, c'est parce que le propriétaire de l'objet est actualisé et recréé à chaque fois que son état change. Essayez d'utiliser @StateObject si votre application est iOS 14 et versions ultérieures. Il empêche la recréation de l'objet lors de l'actualisation de la vue. https://developer.apple.com/documentation/swiftui/stateobject

Lorsqu'une vue crée sa propre instance @ObservedObject, elle est recréée chaque fois qu'une vue est supprimée et redessinée. Au contraire une variable @State gardera sa valeur lorsqu'une vue est redessinée. Un @StateObject est une combinaison de @ObservedObject et @State - l'instance du ViewModel sera conservée et réutilisée même après qu'une vue soit supprimée et redessinée

Quelle est la différence entre ObservedObject et StateObject dans SwiftUI

1
ada10086 3 mars 2021 à 18:48