web-dev-qa-db-fra.com

SwiftUI NavigationLink charge la vue de destination immédiatement, sans cliquer

Avec le code suivant:

struct HomeView: View {
    var body: some View {
        NavigationView {
            List(dataTypes) { dataType in
                NavigationLink(destination: AnotherView()) {
                    HomeViewRow(dataType: dataType)
                }
            }
        }
    }
}

Ce qui est bizarre, quand HomeView apparaît, NavigationLink charge immédiatement le AnotherView. Par conséquent, toutes les dépendances AnotherView sont également chargées, même si elles ne sont pas encore visibles à l'écran. L'utilisateur doit cliquer sur la ligne pour la faire apparaître. Mon AnotherView contient un DataSource, où diverses choses se produisent. Le problème est que l'ensemble DataSource est chargé à ce stade, y compris certains temporisateurs, etc.

Est-ce que je fais quelque chose de mal..? Comment le gérer de telle manière que AnotherView se charge une fois que l'utilisateur appuie sur cette HomeViewRow?

12
Vive

Il faut un ForEach personnalisé pour faire ce que vous demandez car le générateur de fonctions doit évaluer l'expression

NavigationLink(destination: AnotherView()) {
    HomeViewRow(dataType: dataType)
}

pour que chaque ligne visible puisse afficher HomeViewRow(dataType:), auquel cas AnotherView() doit également être initialisé.

Pour éviter cela, un ForEach personnalisé est nécessaire.

import SwiftUI

struct LoadLaterView: View {
    var body: some View {
        HomeView()
    }
}

struct DataType: Identifiable {
    let id = UUID()
    var i: Int
}

struct ForEachLazyNavigationLink<Data: RandomAccessCollection, Content: View, Destination: View>: View where Data.Element: Identifiable {
    var data: Data
    var destination: (Data.Element) -> (Destination)
    var content: (Data.Element) -> (Content)

    @State var selected: Data.Element? = nil
    @State var active: Bool = false

    var body: some View {
        VStack{
            NavigationLink(destination: {
                VStack{
                    if self.selected != nil {
                        self.destination(self.selected!)
                    } else {
                        EmptyView()
                    }
                }
            }(), isActive: $active){
                Text("Hidden navigation link")
                    .background(Color.orange)
                    .hidden()
            }
            List{
                ForEach(data) { (element: Data.Element) in
                    Button(action: {
                        self.selected = element
                        self.active = true
                    }) { self.content(element) }
                }
            }
        }
    }
}

struct HomeView: View {
    @State var dataTypes: [DataType] = {
        return (0...99).map{
            return DataType(i: $0)
        }
    }()

    var body: some View {
        NavigationView{
            ForEachLazyNavigationLink(data: dataTypes, destination: {
                return AnotherView(i: $0.i)
            }, content: {
                return HomeViewRow(dataType: $0)
            })
        }
    }
}

struct HomeViewRow: View {
    var dataType: DataType

    var body: some View {
        Text("Home View \(dataType.i)")
    }
}

struct AnotherView: View {
    init(i: Int) {
        print("Init AnotherView \(i.description)")
        self.i = i
    }

    var i: Int
    var body: some View {
        print("Loading AnotherView \(i.description)")
        return Text("hello \(i.description)").onAppear {
            print("onAppear AnotherView \(self.i.description)")
        }
    }
}
3
Fabian

J'ai eu le même problème où j'aurais pu avoir une liste de 50 éléments, qui a ensuite chargé 50 vues pour la vue de détail qui appelait une API (ce qui a entraîné le téléchargement de 50 images supplémentaires).

La réponse pour moi a été d'utiliser .onAppear pour déclencher toute la logique qui doit être exécutée lorsque la vue apparaît à l'écran (comme désactiver vos minuteries).

struct AnotherView: View {
    var body: some View {
        VStack{
            Text("Hello World!")
        }.onAppear {
            print("I only printed when the view appeared")
            // trigger whatever you need to here instead of on init
        }
    }
}
3
Ernest Cunningham