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
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.
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 {
// ...
}
}
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()
}
}
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()
}
}
}
}
}
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")
}
}