Present a new view in SwiftUI Present a new view in SwiftUI ios ios

Present a new view in SwiftUI


To show a modal (iOS 13 style)

You just need a simple sheet with the ability to dismiss itself:

struct ModalView: View {    @Binding var presentedAsModal: Bool    var body: some View {        Button("dismiss") { self.presentedAsModal = false }    }}

And present it like:

struct ContentView: View {    @State var presentingModal = false        var body: some View {        Button("Present") { self.presentingModal = true }        .sheet(isPresented: $presentingModal) { ModalView(presentedAsModal: self.$presentingModal) }    }}

Note that I passed the presentingModal to the modal so you can dismiss it from the modal itself, but you can get rid of it.


To make it REALLY present fullscreen (Not just visually)

You need to access to the ViewController. So you need some helper containers and environment stuff:

struct ViewControllerHolder {    weak var value: UIViewController?}struct ViewControllerKey: EnvironmentKey {    static var defaultValue: ViewControllerHolder {        return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)    }}extension EnvironmentValues {    var viewController: UIViewController? {        get { return self[ViewControllerKey.self].value }        set { self[ViewControllerKey.self].value = newValue }    }}

Then you should use implement this extension:

extension UIViewController {    func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {        let toPresent = UIHostingController(rootView: AnyView(EmptyView()))        toPresent.modalPresentationStyle = style        toPresent.rootView = AnyView(            builder()                .environment(\.viewController, toPresent)        )        NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "dismissModal"), object: nil, queue: nil) { [weak toPresent] _ in            toPresent?.dismiss(animated: true, completion: nil)        }        self.present(toPresent, animated: true, completion: nil)    }}

Finally

you can make it fullscreen like:

struct ContentView: View {    @Environment(\.viewController) private var viewControllerHolder: UIViewController?        var body: some View {        Button("Login") {            self.viewControllerHolder?.present(style: .fullScreen) {                Text("Main") // Or any other view you like// uncomment and add the below button for dismissing the modal            // Button("Cancel") {            //       NotificationCenter.default.post(name: Notification.Name(rawValue: "dismissModal"), object: nil)            //        }            }        }    }}


For iOS 14 and Xcode 12:

struct ContentView: View {    @State private var isPresented = falsevar body: some View {    Button("Show Modal with full screen") {        self.isPresented.toggle()    }    .fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)    }}
struct FullScreenModalView: View {     @Environment(\.presentationMode) var presentationModevar body: some View {    VStack {        Text("This is a modal view")    }    .frame(maxWidth: .infinity, maxHeight: .infinity)    .background(Color.red)    .edgesIgnoringSafeArea(.all)    .onTapGesture {        presentationMode.wrappedValue.dismiss()    }}}

Hope this answer can help you all! Comment below about your result.

Ref: This Link


Disclaimer: Below is not really like a "native modal", neither behave nor look&feel, but if anyone would need a custom transition of one view over other, making active only top one, the following approach might be helpful.

So, if you expect something like the following

custom SwiftUI modal

Here is a simple code for demo the approach (of corse animation & transition parameters can be changed by wish)

struct ModalView : View {    @Binding var activeModal: Bool    var body : some View {        VStack {            Button(action: {                withAnimation(.easeInOut(duration: 0.3)) {                    self.activeModal = false                }            }) {                Text("Hide modal")            }            Text("Modal View")        }        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)        .background(Color.green)    }}struct MainView : View {    @Binding var activeModal: Bool    var body : some View {        VStack {            Button(action: {                withAnimation(.easeInOut(duration: 0.3)) {                    self.activeModal = true                }            }) {                Text("Show modal")            }            Text("Main View")        }        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)        .background(Color.yellow)    }}struct ModalContainer: View {    @State var showingModal = false    var body: some View {        ZStack {            MainView(activeModal: $showingModal)                .allowsHitTesting(!showingModal)            if showingModal {                ModalView(activeModal: $showingModal)                    .transition(.move(edge: .bottom))                    .zIndex(1)            }        }    }}