web-dev-qa-db-fra.com

Macos Swiftui Navigation pour une vue unique

Je tente de créer une vue Paramètres pour ma barre d'état Macos Swiftui. Ma mise en œuvre jusqu'à présent utilise un NavigationView et NavigationLink, mais cette solution produit une demi-vue à mesure que la vue Paramètres appuie la vue parent sur le côté. Screenshot et exemple de code ci-dessous.

Barre latérale de la navigation

enter image description here

struct ContentView: View {
    var body: some View {
        VStack{
            NavigationView{
            NavigationLink(destination: SecondView()){
                Text("Go to next view")
                }}
        }.frame(width: 800, height: 600, alignment: .center)}
}

struct SecondView: View {
    var body: some View {
        VStack{

                Text("This is the second view")

        }.frame(width: 800, height: 600, alignment: .center)
    }
}

Les petites informations que je peux trouver suggèrent que cela est inévitable à l'aide de SWIFTUI sur MacOS, car l'écran complet 'NavigationView sur iOS (StackNavigationViewStyle) n'est pas disponible sur MacOS.

Existe-t-il un moyen simple ou même complexe d'implémenter une transition vers une vue de contexte qui prend tout le cadre à Swiftui pour MacOS? Et sinon, est-il possible d'utiliser AppKit pour appeler un objet de vision écrit à Swiftui?

Aussi a Swift Newbie - s'il vous plaît soyez doux.

10
Husker28

J'ai élargi Asperi Great Suggestion et créé un générique, réutilisable StackNavigationView pour MacOS (ou même iOS, si vous le souhaitez). Quelques points forts:

  • Il prend en charge tout nombre de sous-espions (dans n'importe quelle mise en page).
  • Il ajoute automatiquement un bouton "Retour" pour chaque sous-vue (juste un texte pour l'instant, mais vous pouvez échanger dans une icône si vous utilisez MacOS 11+).

Example view on macOS

SWIFT V5.2:

struct StackNavigationView<RootContent, SubviewContent>: View where RootContent: View, SubviewContent: View {
    
    @Binding var currentSubviewIndex: Int
    @Binding var showingSubview: Bool
    let subviewByIndex: (Int) -> SubviewContent
    let rootView: () -> RootContent
    
    var body: some View {
        VStack {
            VStack{
                if !showingSubview { // Root view
                    rootView()
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .transition(AnyTransition.move(Edge: .leading)).animation(.default)
                }
                if showingSubview { // Correct subview for current index
                    StackNavigationSubview(isVisible: self.$showingSubview) {
                        self.subviewByIndex(self.currentSubviewIndex)
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .transition(AnyTransition.move(Edge: .trailing)).animation(.default)
                }
            }
        }
    }
    
    init(currentSubviewIndex: Binding<Int>, showingSubview: Binding<Bool>, @ViewBuilder subviewByIndex: @escaping (Int) -> SubviewContent, @ViewBuilder rootView: @escaping () -> RootContent) {
        self._currentSubviewIndex = currentSubviewIndex
        self._showingSubview = showingSubview
        self.subviewByIndex = subviewByIndex
        self.rootView = rootView
    }
    
    private struct StackNavigationSubview<Content>: View where Content: View {
        
        @Binding var isVisible: Bool
        let contentView: () -> Content
        
        var body: some View {
            VStack {
                HStack { // Back button
                    Button(action: {
                        self.isVisible = false
                    }) {
                        Text("< Back")
                    }.buttonStyle(BorderlessButtonStyle())
                    Spacer()
                }
                .padding(.horizontal).padding(.vertical, 4)
                contentView() // Main view content
            }
        }
    }
}

Plus d'infos sur @ViewBuilder et les génériques utilisés peuvent être trouvés ici .

Voici un exemple de base de celui-ci en cours d'utilisation. La vue parent suit la sélection actuelle et l'état d'affichage (à l'aide de @State), permettant à quoi que ce soit dans ses sous-espèces de déclencher des changements d'état.

struct ExampleView: View {
    
    @State private var currentSubviewIndex = 0
    @State private var showingSubview = false
    
    var body: some View {
        StackNavigationView(
            currentSubviewIndex: self.$currentSubviewIndex,
            showingSubview: self.$showingSubview,
            subviewByIndex: { index in
                self.subView(forIndex: index)
            }
        ) {
            VStack {
                Button(action: { self.showSubview(withIndex: 0) }) {
                    Text("Show View 1")
                }
                Button(action: { self.showSubview(withIndex: 1) }) {
                    Text("Show View 2")
                }
                Button(action: { self.showSubview(withIndex: 2) }) {
                    Text("Show View 3")
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.blue)
        }
    }
    
    private func subView(forIndex index: Int) -> AnyView {
        switch index {
        case 0: return AnyView(Text("I'm View One").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.green))
        case 1: return AnyView(Text("I'm View Two").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.yellow))
        case 2: return AnyView(VStack {
            Text("And I'm...")
            Text("View Three")
        }.frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.orange))
        default: return AnyView(Text("Inavlid Selection").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red))
        }
    }
    
    private func showSubview(withIndex index: Int) {
        currentSubviewIndex = index
        showingSubview = true
    }
}

Remarque: Les génériques comme celui-ci nécessitent que toutes les sous-étapes soient du même type. Si ce n'est pas le cas, vous pouvez les envelopper AnyView, comme je l'ai fait ici. Le wrapper AnyView n'est pas nécessaire si vous utilisez un type cohérent pour toutes les sous-espions (le type de la vue racine n'a pas besoin de correspondre).

1
TheNeil

Vous pouvez obtenir une navigation en plein écran avec

 .navigationViewStyle(StackNavigationViewStyle())
0
Volker88