loading...

How to Present and Dismiss a Modal in SwiftUI

maeganwilson_ profile image Maegan Wilson Originally published at appsbymw.com Updated on ・4 min read

How to Present and Dismiss a Modal in SwiftUI

In this post, we will cover how to present and dismiss a modal view.

Find the source code at this Github Repo.

GitHub logo maeganjwilson / swiftui-show-modal-tutorial

A tutorial for SwiftUI on how to present and dismiss a Modal

swiftui-show-modal-tutorial

A tutorial for SwiftUI on how to present and dismiss a Modal




NOTE: this tutorial is using Xcode 11 and has been tested using iOS 13.1.2.

Let's get started by making a new project using SwiftUI. When creating a new project, make sure that the language is set to Swift, and the User Interface is configured to SwiftUI like in the picture below.

show_modal variable

Now that the project is made, we need to open the ContentView.swift file to declare a variable that determines whether or not to present the modal.

struct ContentView: View {
    // Declare this state variable below
    @State private var show_modal: Bool = false

    var body: some View {
        Text("Hello World")
    }
}

The variable has to be a binding variable and one that can update the view when changing, which is why we declare it as a State variable.

Button to change the state!

Let's change the Text() to a Button() that sets show_modal to true.

Button(action: {
        print("Button Pushed")
        self.show_modal = true
    }) {
        Text("Show Modal")
}

I added a print() statement to make sure the button works.
Go ahead and run the app (Command + R) and click on the button. By clicking on the button, "Button Pushed" will be printed in the console.

Create the Modal View

Now, let's create the modal view. Create a new file and change the Text to "This is a Modal." It should look like the code snippet below.

import SwiftUI

struct ModalView: View {
    var body: some View {
        Text("This is a modal")
    }
}

Making the Modal Appear

It's time to make the modal appear from the button push on the first view. Open ContentView.swift and add the following to the button.

Button(action: {
        print("Button Pushed")
        self.show_modal = true
    }) {
        Text("Show Modal")
    }.sheet(isPresented: self.$show_modal) {
         ModalView()
        }

What does .sheet() // more code do? It is deciding if ModalView should be presented when show_modal changes.

.sheet(isPresented: Binding<Bool>){ /* View to present */} is a modifier that can present the view when isPresented is true. In our example, show_modal is a Binding<Bool> because it is declared with @State. We also set the view to be presented as ModalView().

ContentView.swift should now be complete and look like this.

import SwiftUI

struct ContentView: View {
    @State private var show_modal: Bool = false

    var body: some View {
        Button(action: {
            print("Button Pushed")
            self.show_modal = true
        }) {
            Text("Show Modal")
        }.sheet(isPresented: self.$show_modal) {
            ModalView()
        }
    }
}

Running the application right now, we can dismiss the modal by dragging down from the top of the modal.

Add a dismiss button

Dragging works to dismiss, but sometimes users would instead hit a button, or maybe you want the user to hit the button to confirm, and a drag cancels the operation. To add a button to dismiss the modal, we need to put add an Environment variable for presentationMode and then call presentationMode.wrappedValue.dismiss(). Here is how it looks in ModalView.swift.

struct ModalView: View {
    // 1. Add the environment variable
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        // 2. Embed Text in a VStack
        VStack {
            // 3. Add a button with the following action
            Button(action: {
                print("dismisses form")
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Dismiss")
            }.padding(.bottom, 50)
            Text("This is a modal")
        }
    }
}

I'm going to break down what we did.

  1. Add the environment variable. This environment variable is what determines when to dismiss the modal.
  2. Embed the Text in a VStack. We do this to be able to have a Button view on top of the Text.
  3. Add a button with the following action. The action in the button is what to perform when the button is tapped. self.presentaionMode.wrappedValue.dismiss() is the method that dismisses the Modal.

Now, running the application, we can dismiss the view either by dragging or by tapping on the button.


If you enjoy my posts, please consider sharing it or Buying me a Coffee!

Buy Me A Coffee


EDIT #1:

I was asked by @cwhooten how to make a list that presents a dynamic modal. You can find that answer as a branch to the original example by clicking on this sentence.

EDIT #2:

Updated the note at the top.

EDIT #3:

Updated the note at the top since it has been tested with Release Xcode and Release iOS 13.

Discussion

pic
Editor guide
Collapse
careerunderdog profile image
Lance

For me, I can only present the sheet once when dismissing with the button. If I try to present it again it does nothing, but if I drag to dismiss I can keep presenting and dismissing repeatedly. Any suggestions?

Collapse
maeganwilson_ profile image
Maegan Wilson Author

I'm running this on a device and in the Xcode simulator, and I'm not experiencing this. Any chance you have a link or some code to look at?

Are you running Catalina? I did have the issue you are reporting with the Catalina betas sometimes, but every time I ran on a real device it worked.

Collapse
careerunderdog profile image
Lance

Well, after some more testing, it works as expected unless I am using a button in navigation bar trailing items. For some reason the problem I described only happens when it's a navigation bar button. If I just put a button somewhere else in the View it works perfectly. This solves my problem for now. Thanks for the reply, and for the tutorial!

And yes I am running Catalina, still on a beta so I'll try again when I am on the real Catalina/Xcode.

Thread Thread
maeganwilson_ profile image
Maegan Wilson Author

I also had this problem in the simulator at some point in the beta process, but when on a real device it works.
In my app on the App Store, I have this implemented as a nav bar button and it works as expected. You can see the code on my GitHub. I linked to the direct file.

Collapse
kevindorfer profile image
Collapse
maeganwilson_ profile image