SwiftUI - Is there a popViewController equivalent in SwiftUI?

后端 未结 13 2074
说谎
说谎 2020-12-12 20:18

I was playing around with SwiftUI and want to be able to come back to the previous view when tapping a button, the same we use popViewController inside a

相关标签:
13条回答
  • 2020-12-12 21:07

    Update: the NavigationDestinationLink API in this solution has been deprecated as of iOS 13 Beta 5. It is now recommended to use NavigationLink with an isActive binding.

    I figured out a solution for programmatic pushing/popping of views in a NavigationView using NavigationDestinationLink.

    Here's a simple example:

    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()
            }
        }
    }
    

    I wrote about this in a blog post here.

    0 讨论(0)
  • 2020-12-12 21:09

    EDIT: This answer over here is better than mine, but both work: SwiftUI dismiss modal

    What you really want (or should want) is a modal presentation, which several people have mentioned here. If you go that path, you definitely will need to be able to programmatically dismiss the modal, and Erica Sadun has a great example of how to do that here: https://ericasadun.com/2019/06/16/swiftui-modal-presentation/

    Given the difference between declarative coding and imperative coding, the solution there may be non-obvious (toggling a bool to false to dismiss the modal, for example), but it makes sense if your model state is the source of truth, rather than the state of the UI itself.

    Here's my quick take on Erica's example, using a binding passed into the TestModal so that it can dismiss itself without having to be a member of the ContentView itself (as Erica's is, for simplicity).

    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)
        }
    }
    
    0 讨论(0)
  • 2020-12-12 21:10

    There is now a way to programmatically pop in a NavigationView, if you would like. This is in beta 5.

    Notice that you don't need the back button. You could programmatically trigger the showSelf property in the DetailView any way you like. And you don't have to display the "Push" text in the master. That could be an EmptyView(), thereby creating an invisible segue.

    (The new NavigationLink functionality takes over the deprecated NavigationDestinationLink)

    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
    
    0 讨论(0)
  • 2020-12-12 21:12

    With State Variables. Try that.

    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()
            }
        }
    }
    

    0 讨论(0)
  • 2020-12-12 21:17

    Modify your BView struct as follows. The button will perform just as popViewController did in 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") }
        }
    }
    
    0 讨论(0)
  • 2020-12-12 21:17

    Xcode 11.3

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
    ...
    
    // If animation is strange, try this.
    DispatchQueue.main.async {
        self.presentationMode.wrappedValue.dismiss()
    }
    
    0 讨论(0)
提交回复
热议问题