web-dev-qa-db-fra.com

SwiftUI - Existe-t-il un équivalent popViewController dans SwiftUI?

Je jouais avec SwiftUI et je veux pouvoir revenir à la vue précédente en appuyant sur un bouton, de la même manière que nous utilisons popViewController dans un UINavigationController. Y a-t-il un moyen prévu de le faire jusqu'à présent?

J'ai également essayé d'utiliser NavigationDestinationLink pour le faire sans succès.

struct AView: View {
    var body: some View {
        NavigationView {
            NavigationButton(destination: BView()) {
                Text("Go to B")
            }
        }
    }
}

struct BView: View {
    var body: some View {
        Button(action: {
            // Trying to go back to the previous view
            // previously: navigationController.popViewController(animated: true)
        }) {
            Text("Come back to A")
        }
    }
}
41
Alexandre Legent

Modifiez votre structure BView comme suit. Le bouton fonctionnera exactement comme le faisait popViewController dans UIKit.

struct BView: View {
    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
    var body: some View {
        Button(action: { self.mode.wrappedValue.dismiss() })
        { Text("Come back to A") }
    }
}
24
Chuck H

Il existe maintenant un moyen d'insérer par programmation une NavigationView, si vous le souhaitez. C'est en version bêta 5.

Notez que vous n'avez pas besoin du bouton de retour. Vous pouvez déclencher par programme la propriété showSelf dans DetailView comme vous le souhaitez. Et vous n'avez pas besoin d'afficher le texte "Push" dans le masque. Cela pourrait être un EmptyView (), créant ainsi une séquence invisible.

(La nouvelle fonctionnalité NavigationLink prend le relais de la NavigationDestinationLink obsolète)

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            MasterView()
        }
    }
}

struct MasterView: View {
    @State var showDetail = false

    var body: some View {
        VStack {
            NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
                Text("Push")
            }
        }
    }
}

struct DetailView: View {
    @Binding var showSelf: Bool

    var body: some View {
        Button(action: {
            self.showSelf = false
        }) {
            Text("Pop")
        }
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif
6
MScottWaller

Cela semble fonctionner pour moi sur watchOS (je n'ai pas essayé sur iOS):

@Environment(\.presentationMode) var presentationMode

Et puis quand tu as besoin de sauter

self.presentationMode.wrappedValue.dismiss()
4
Cherpak Evgeny

Il semble qu'une tonne de fonctionnalités de navigation de base soit super boguée, ce qui est décevant et peut valoir la peine d'être abandonné pour l'instant pour économiser des heures de frustration. Pour moi, PresentationButton est le seul qui fonctionne. Les onglets TabbedView ne fonctionnent pas correctement et NavigationButton ne fonctionne pas du tout pour moi. Sonne comme YMMV si NavigationButton fonctionne pour vous.

J'espère qu'ils le corrigent en même temps qu'ils corrigent la saisie semi-automatique, ce qui nous donnerait une bien meilleure idée de ce qui est disponible pour nous. En attendant, je le code à contrecœur et je prends des notes pour les correctifs. Il est nul de devoir déterminer si nous faisons quelque chose de mal ou si cela ne fonctionne tout simplement pas, mais c'est la version bêta pour vous!

4
Sean C. Li

Mise à jour: l'API NavigationDestinationLink dans cette solution est obsolète à partir d'iOS 13 Beta 5. Il est maintenant recommandé d'utiliser NavigationLink avec une liaison isActive.

J'ai trouvé une solution pour pousser/éclater par programme des vues dans une NavigationView en utilisant NavigationDestinationLink.

Voici un exemple simple:

import Combine
import SwiftUI

struct DetailView: View {
    var onDismiss: () -> Void

    var body: some View {
        Button(
            "Here are details. Tap to go back.",
            action: self.onDismiss
        )
    }
}

struct MainView: View {
    var link: NavigationDestinationLink<DetailView>
    var publisher: AnyPublisher<Void, Never>

    init() {
        let publisher = PassthroughSubject<Void, Never>()
        self.link = NavigationDestinationLink(
            DetailView(onDismiss: { publisher.send() }),
            isDetail: false
        )
        self.publisher = publisher.eraseToAnyPublisher()
    }

    var body: some View {
        VStack {
            Button("I am root. Tap for more details.", action: {
                self.link.presented?.value = true
            })
        }
            .onReceive(publisher, perform: { _ in
                self.link.presented?.value = false
            })
    }
}

struct RootView: View {
    var body: some View {
        NavigationView {
            MainView()
        }
    }
}

J'ai écrit à ce sujet dans un article de blog ici .

3
Ryan Ashcraft

Avec des variables d'état. Essayez ça.

struct ContentViewRoot: View {
    @State var pushed: Bool = false
    var body: some View {
        NavigationView{
            VStack{
                NavigationLink(destination:ContentViewFirst(pushed: self.$pushed), isActive: self.$pushed) { EmptyView() }
                    .navigationBarTitle("Root")
                Button("Push"){
                    self.pushed = true
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}


struct ContentViewFirst: View {
    @Binding var pushed: Bool
    @State var secondPushed: Bool = false
    var body: some View {
        VStack{
            NavigationLink(destination: ContentViewSecond(pushed: self.$pushed, secondPushed: self.$secondPushed), isActive: self.$secondPushed) { EmptyView() }
                .navigationBarTitle("1st")
            Button("Push"){
                self.secondPushed = true;
            }
        }
    }
}



struct ContentViewSecond: View {
    @Binding var pushed: Bool
    @Binding var secondPushed: Bool

    var body: some View {
        VStack{
            Spacer()
            Button("PopToRoot"){
                self.pushed = false
            } .navigationBarTitle("2st")

            Spacer()
            Button("Pop"){
                         self.secondPushed = false
                     } .navigationBarTitle("1st")
            Spacer()
        }
    }
}

enter image description here

2
Andreas Stokidis

Ci-dessous fonctionne pour moi dans XCode11 GM

self.myPresentationMode.wrappedValue.dismiss()
1
guru

EDIT: Cette réponse ici est meilleure que la mienne, mais les deux fonctionnent: SwiftUI rejette le modal

Ce que vous voulez vraiment (ou devriez vouloir) est une présentation modale, que plusieurs personnes ont mentionnée ici. Si vous suivez cette voie, vous devrez certainement être en mesure de rejeter le modal par programme, et Erica Sadun a un excellent exemple de la façon de le faire ici: https://ericasadun.com/2019/06/16/swiftui-modal-presentation /

Étant donné la différence entre le codage déclaratif et le codage impératif, la solution peut être non évidente (basculer un booléen sur faux pour ignorer le modal, par exemple), mais cela a du sens si votre état de modèle est la source de la vérité, plutôt que le l'état de l'interface utilisateur elle-même.

Voici mon rapide aperçu de l'exemple d'Erica, en utilisant une liaison passée dans le TestModal afin qu'il puisse se rejeter sans avoir à être membre du ContentView lui-même (comme celui d'Erica, pour plus de simplicité).

struct TestModal: View {
    @State var isPresented: Binding<Bool>

    var body: some View {
        Button(action: { self.isPresented.value = false }, label: { Text("Done") })
    }
}

struct ContentView : View {
    @State var modalPresented = false

    var body: some View {
        NavigationView {
            Text("Hello World")
            .navigationBarTitle(Text("View"))
            .navigationBarItems(trailing:
                Button(action: { self.modalPresented = true }) { Text("Show Modal") })
        }
        .presentation(self.modalPresented ? Modal(TestModal(isPresented: $modalPresented)) {
            self.modalPresented.toggle()
        } : nil)
    }
}
1
Brad

Utilisez @Environment(\.presentationMode) var presentationMode pour revenir à la vue précédente. Consultez le code ci-dessous pour plus de compréhension.

import SwiftUI

struct ContentView: View {


    var body: some View {

        NavigationView {
            ZStack {
                Color.gray.opacity(0.2)

                NavigationLink(destination: NextView(), label: {Text("Go to Next View").font(.largeTitle)})
            }.navigationBarTitle(Text("This is Navigation"), displayMode: .large)
                .edgesIgnoringSafeArea(.bottom)
        }
    }
}

struct NextView: View {
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        ZStack {
            Color.gray.opacity(0.2)
        }.navigationBarBackButtonHidden(true)
            .navigationBarItems(leading: Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }, label: { Image(systemName: "arrow.left") }))
            .navigationBarTitle("", displayMode: .inline)
    }
}


struct NameRow: View {
    var name: String
    var body: some View {
        HStack {
            Image(systemName: "circle.fill").foregroundColor(Color.green)
            Text(name)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
0
Ashish Kanani

Xcode 11.3

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

...

// If animation is strange, try this.
DispatchQueue.main.async {
    self.presentationMode.wrappedValue.dismiss()
}
0
hstdt

au lieu de NavigationButton, utilisez Navigation DestinationLink

mais vous devez importer Combine

struct AView: View {
 var link: NavigationDestinationLink<BView>
var publisher: AnyPublisher<Void, Never>

init() {
    let publisher = PassthroughSubject<Void, Never>()
    self.link = NavigationDestinationLink(
        BView(onDismiss: { publisher.send() }),
        isDetail: false
    )
    self.publisher = publisher.eraseToAnyPublisher()
}

var body: some View {
    NavigationView {
        Button(action:{
        self.link.presented?.value = true


 }) {
            Text("Go to B")
        }.onReceive(publisher, perform: { _ in
            self.link.presented?.value = false
        })
    }
}
}

struct BView: View {
var onDismiss: () -> Void
var body: some View {
    Button(action: self.onDismiss) {
        Text("Come back to A")
    }
}
}
0
Alireza12t