39
votes

Vue SwiftUI - viewDidLoad ()?

Tentant de charger une image après le chargement de la vue, l'objet modèle pilotant la vue (voir MovieDetail ci-dessous) a une urlString. Parce qu'un élément SwiftUI View n'a pas de méthode de cycle de vie (et qu'il n'y a pas de contrôleur de vue pilotant les choses), quelle est la meilleure façon de gérer cela?

Le principal problème que j'ai est quelle que soit la manière dont j'essaie de résoudre le problème (liaison d'un objet ou utilisation d'une variable State), ma vue n'a pas l' urlString avant son chargement ...

// detail view that needs to make the asynchronous call
struct MovieDetail : View {

    let movie: Movie
    @State var imageObject = BoundImageObject()

    var body: some View {
        HStack(alignment: .top) {
            VStack {
                Image(uiImage: imageObject.image)
                    .scaledToFit()

                Text(movie.title)
                    .font(.subheadline)
            }
        }
    }
}
// root content list view that navigates to the detail view
struct ContentView : View {

    var movies: [Movie]

    var body: some View {
        NavigationView {
            List(movies) { movie in
                NavigationButton(destination: MovieDetail(movie: movie)) {
                    MovieRow(movie: movie)
                }
            }
            .navigationBarTitle(Text("Star Wars Movies"))
        }
    }
}
// movie object
struct Movie: Decodable, Identifiable {

    let id: String
    let title: String
    let year: String
    let type: String
    var posterUrl: String

    private enum CodingKeys: String, CodingKey {
        case id = "imdbID"
        case title = "Title"
        case year = "Year"
        case type = "Type"
        case posterUrl = "Poster"
    }
}

Merci d'avance.


0 commentaires

3 Réponses :


46
votes

J'espère que ceci est utile. J'ai trouvé un article de blog qui parle de faire des choses surAppear pour une vue de navigation.

L'idée serait que vous intégriez votre service dans un objet BindableObject et que vous vous abonniez à ces mises à jour dans votre vue.

import SwiftUI
import Combine

class ReposStore: BindableObject {
    var repos: [Repo] = [] {
        didSet {
            didChange.send(self)
        }
    }

    var didChange = PassthroughSubject<ReposStore, Never>()

    let service: GithubService
    init(service: GithubService) {
        self.service = service
    }

    func fetch(matching query: String) {
        service.search(matching: query) { [weak self] result in
            DispatchQueue.main.async {
                switch result {
                case .success(let repos): self?.repos = repos
                case .failure: self?.repos = []
                }
            }
        }
    }
}
struct SearchView : View {
    @State private var query: String = "Swift"
    @EnvironmentObject var repoStore: ReposStore

    var body: some View {
        NavigationView {
            List {
                TextField($query, placeholder: Text("type something..."), onCommit: fetch)
                ForEach(repoStore.repos) { repo in
                    RepoRow(repo: repo)
                }
            }.navigationBarTitle(Text("Search"))
        }.onAppear(perform: fetch)
    }

    private func fetch() {
        repoStore.fetch(matching: query)
    }
}

Crédit à: Majid Jabrayilov


1 commentaires

Corrigez-moi si je me trompe, mais l'utilisation de la fetch dans onAppear provoque une requête réseau à chaque fois que la vue apparaît. (par exemple dans un TabView).



12
votes

Entièrement mis à jour pour Xcode 11.2, Swift 5.0

Je pense que le viewDidLoad() juste égal à implémenter dans la fermeture du corps.
SwiftUI nous donne des équivalents à viewDidAppear viewDidAppear() et viewDidDisappear() sous la forme de onAppear() et onDisappear() . Vous pouvez attacher n'importe quel code à ces deux événements que vous voulez, et SwiftUI les exécutera lorsqu'ils se produiront.

À titre d'exemple, cela crée deux vues qui utilisent onAppear() et onDisappear() pour imprimer des messages, avec un lien de navigation pour se déplacer entre les deux:

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView()) {
                    Text("Hello World")
                }
            }
        }.onAppear {
            print("ContentView appeared!")
        }.onDisappear {
            print("ContentView disappeared!")
        }
    }
}

réf: https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear


0 commentaires

1
votes

Cette solution fonctionne UNIQUEMENT si la structure de la vue n'est pas modifiée (vous n'ajoutez / supprimez pas de sous-vues). Cependant, vous pouvez toujours changer l'état des vues (comme la position, les animer).

Créer l'extension View :

struct SomeView: View {
    var body: some View {
        VStack {
            Text("HELLO!")
        }.onLoad {
            print("onLoad")
        }
    }
}

Ensuite, utilisez-le comme ceci:

extension View {
    func onLoad(perform action: (() -> Void)? = nil) -> some View {
        var actionPerformed = false
        return self.onAppear {
            guard !actionPerformed else {
                return
            }
            actionPerformed = true
            action?()
        }
    }
}

Je n'ai pas trop testé ma solution, mais elle semble fonctionner correctement. «onLoad» ne sera imprimé qu'une seule fois même si vous revenez d'une autre vue dans la pile de navigation.


3 commentaires

pourquoi ça marche? Je pense que l'actionPerformed serait écrasée à chaque fois que la vue se recharge?


D'après ce que je comprends, SwiftUI ne recrée pas chaque vue à chaque fois que quelque chose change. Seules les vues affectées sont mises à jour / créées de nouveau. Donc, si nous appliquons onLoad à la vue racine, cela fonctionnera. Mais comme je l'ai dit - je n'ai pas trop testé cela.


Ok, fait quelques tests supplémentaires. Cette approche ne fonctionne que si vous n'ajoutez / supprimez pas de vues. Vous pouvez toujours modifier l'état des vues (comme les déplacer, les animer).