Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dismiss a presenting view to the root view of tab view in SwiftUI?

I'm using TabView on my home page. Let's just say I have 4 tabs. On second tab, i can go to another view using NavigationLink and I go to another 2 views using NavigationLink. Then on the latest view, there is a button to present a view and i use .fullScreenCover (since I want to present it full screen).

In the presenting view, I add an X mark on the left side of the navigationBarItems to dismiss. I use @Environment(\.presentationMode) var presentationMode and presentationMode.wrappedValue.dismiss() to dismiss. But it only dismiss the presenting view to the previous view, while actually I want to dismiss it to the root of my view which is the 2nd tab of my TabView.

Is there a way to do this? Because I have looked up to some articles and nothing relevant especially in TabView context.

I also have a question tho:

  1. Is it a right approach to use .fullScreenCover? Or is there another possible solution for example presenting a modal with full screen style (if there's any cause i'm not sure either).

Any suggestions will be very appreciated, thankyou in advance.

like image 496
Aldo Sugiarto Avatar asked Apr 09 '21 02:04

Aldo Sugiarto


People also ask

How do I dismiss to root view in SwiftUI?

wrappedValue. dismiss() from that child view will pop to the root view.

How do I use tab view in SwiftUI?

Press Cmd+N to create a new SwiftUI View, calling it “MainView”. Creating tabs is as easy as putting different views inside an instance of TabView , but in order to add an image and text to the tab bar item of each view we need to use the tabItem() modifier.


1 Answers

The presentationMode is one-level effect value, ie changing it you close one currently presented screen.

Thus to close many presented screens you have to implement this programmatically, like in demo below.

The possible approach is to use custom EnvironmentKey to pass it down view hierarchy w/o tight coupling of every level view (like with binding) and inject/call only at that level where needed.

Demo tested with Xcode 12.4 / iOS 14.4

demo

struct ContentView: View {
    var body: some View {
        TabView {
            Text("Tab1")
                .tabItem { Image(systemName: "1.square") }
            Tab2RootView()
                .tabItem { Image(systemName: "2.square") }
        }
    }
}

struct Tab2RootView: View {
    @State var toRoot = false
    var body: some View {
        NavigationView {
            Tab2NoteView(level: 0)
                .id(toRoot)          // << reset to root !!
        }
        .environment(\.rewind, $toRoot)        // << inject here !!
    }
}

struct Tab2NoteView: View {
    @Environment(\.rewind) var rewind
    let level: Int

    @State private var showFullScreen = false
    var body: some View {
        VStack {
            Text(level == 0 ? "ROOT" : "Level \(level)")
            NavigationLink("Go Next", destination: Tab2NoteView(level: level + 1))
            Divider()
            Button("Full Screen") { showFullScreen.toggle() }
                .fullScreenCover(isPresented: $showFullScreen,
                                        onDismiss: { rewind.wrappedValue.toggle() }) {
                    Tab2FullScreenView()
                }
        }
    }
}

struct RewindKey: EnvironmentKey {
    static let defaultValue: Binding<Bool> = .constant(false)
}

extension EnvironmentValues {
    var rewind: Binding<Bool> {
        get { self[RewindKey.self] }
        set { self[RewindKey.self] = newValue }
    }
}

struct Tab2FullScreenView: View {
    @Environment(\.presentationMode) var mode

    var body: some View {
        Button("Close") { mode.wrappedValue.dismiss() }
    }
}

backup

like image 127
Asperi Avatar answered Sep 21 '22 09:09

Asperi