web-dev-qa-db-fra.com

Comment passer une vue SwiftUI en tant que variable à une autre structure de vue

J'implémente un très NavigationLink personnalisé appelé MenuItem et je voudrais le réutiliser dans tout le projet. C'est une structure conforme à View et implémente var body : some View qui contient un NavigationLink. Je dois en quelque sorte stocker la vue qui sera présentée par NavigationLink dans le corps de MenuItem mais je ne l'ai pas encore fait.

J'ai défini destinationView dans le corps de MenuItem comme some View et essayé deux initialiseurs:

Cela semblait trop facile:

struct MenuItem: View {
    private var destinationView: some View

    init(destinationView: View) {
        self.destinationView = destinationView
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

-> Erreur: Le protocole 'Affichage' ne peut être utilisé que comme contrainte générique car il a des exigences de type Self ou associées.

2ème essai:

struct MenuItem: View {
    private var destinationView: some View

    init<V>(destinationView: V) where V: View {
        self.destinationView = destinationView
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

-> Erreur: Impossible d'attribuer une valeur de type "V" à "certaines vues".

Essai final:

struct MenuItem: View {
    private var destinationView: some View

    init<V>(destinationView: V) where V: View {
        self.destinationView = destinationView as View
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

-> Erreur: Impossible d'attribuer une valeur de type 'View' à taper 'some View'.

J'espère que quelqu'un pourra m'aider. Il doit y avoir un moyen si NavigationLink peut accepter une vue comme argument. Merci: D

18
Alex

La manière Apple utilise-t-elle des constructeurs de fonctions, il y en a un prédéfini appelé ViewBuilder, créez le dernier argument, ou le seul argument, de votre méthode init pour MenuItem this

...., @ViewBuilder builder: @escaping () -> Content)

l'assigner à une propriété définie quelque chose comme

  let                 viewBuilder: () -> Content

puis là où vous voulez sortir les vues, appelez simplement la fonction comme

HStack {
    viewBuilder()
}

alors tu peux faire

MenuItem {
   Image("myImage")
   Text("My Text")
}

Cela vous permettra de passer jusqu'à 10 vues et à utiliser des conditions etc., mais si vous voulez que ce soit plus restrictif, vous devrez définir votre propre générateur de fonctions. Je ne l'ai pas fait, vous devrez donc faire une recherche sur Google.

11
Nathan Day

Vous devez intégrer le paramètre générique dans MenuItem:

struct MenuItem<Content: View>: View {
    private var destinationView: Content

    init(destinationView: Content) {
        self.destinationView = destinationView
    }

    var body : some View {
        // ...
    }
}
13
rraphael

Vous pouvez créer votre vue personnalisée comme ceci:

struct ENavigationView<Content: View>: View {

    let viewBuilder: () -> Content

    var body: some View {
        NavigationView {
            VStack {
                viewBuilder()
                    .navigationBarTitle("My App")
            }
        }
    }

}

struct ENavigationView_Previews: PreviewProvider {
    static var previews: some View {
        ENavigationView {
            Text("Preview")
        }
    }
}

En utilisant:

struct ContentView: View {

    var body: some View {
        ENavigationView {
            Text("My Text")
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
8
Mickael Belhassen

J'ai vraiment eu du mal à faire fonctionner le mien pour une extension de View. Tous les détails sur la façon de l'appeler sont visibles ici .

L'extension pour View (en utilisant des génériques) - n'oubliez pas de import SwiftUI:

extension View {

    /// Navigate to a new view.
    /// - Parameters:
    ///   - view: View to navigate to.
    ///   - binding: Only navigates when this condition is `true`.
    func navigate<SomeView: View>(to view: SomeView, when binding: Binding<Bool>) -> some View {
        modifier(NavigateModifier(destination: view, binding: binding))
    }
}


// MARK: - NavigateModifier
fileprivate struct NavigateModifier<SomeView: View>: ViewModifier {

    // MARK: Private properties
    fileprivate let destination: SomeView
    @Binding fileprivate var binding: Bool


    // MARK: - View body
    fileprivate func body(content: Content) -> some View {
        NavigationView {
            ZStack {
                content
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                NavigationLink(destination: destination
                    .navigationBarTitle("")
                    .navigationBarHidden(true),
                               isActive: $binding) {
                    EmptyView()
                }
            }
        }
    }
}
1
George_E

Vous pouvez passer un NavigationLink (ou tout autre widget de vue) en tant que variable à une sous-vue comme suit:

import SwiftUI

struct ParentView: View {
    var body: some View {
        NavigationView{

            VStack(spacing: 8){

                ChildView(destinationView: Text("View1"), title: "1st")
                ChildView(destinationView: Text("View2"), title: "2nd")
                ChildView(destinationView: ThirdView(), title: "3rd")
                Spacer()
            }
            .padding(.all)
            .navigationBarTitle("NavigationLinks")
        }
    }
}

struct ChildView<Content: View>: View {
    var destinationView: Content
    var title: String

    init(destinationView: Content,  title: String) {
        self.destinationView = destinationView
        self.title = title
    }

    var body: some View {
        NavigationLink(destination: destinationView){
            Text("This item opens the \(title) view").foregroundColor(Color.black)
        }
    }
}

struct ThirdView: View {
    var body: some View {
        VStack(spacing: 8){

            ChildView(destinationView: Text("View1"), title: "1st")
            ChildView(destinationView: Text("View2"), title: "2nd")
            ChildView(destinationView: ThirdView(), title: "3rd")
            Spacer()
        }
        .padding(.all)
        .navigationBarTitle("NavigationLinks")
    }
}
1
Yarm