Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: close modal

Tags:

swift

swiftui

I know this question has been asked and answered before. Not sure if this changed/broke in Beta 4 for SwiftUI, but I can't seem to get the isPresented solution to work to dismiss a modal shown with sheet.

Here is a simple example of what I tried, I thought this would work, but clicking "Close" does nothing and when I inspect self.isPresented?.value it's nil.

struct DetailView: View {
    @Environment(\.isPresented) var isPresented: Binding<Bool>?
    var body: some View {
        Button(action: {
            self.isPresented?.value = false
        }) {
            Text("Close")
        }
    }
}

struct ContentView: View {
    @State private var showingModal = false
    var body: some View {
        Button(action: {
            self.showingModal = true
        }) {
            Text("Show detail")
        }.sheet(isPresented: $showingModal) {
            DetailView()
        }
    }
}

Update based on suggestion, this works. Seems like too much book keeping to me, hope this gets updated.

struct DetailView: View {
    @Binding var showingModal: Bool
    var body: some View {
        Button(action: {
            self.showingModal = false
        }) {
            Text("Close")
        }
    }
}

struct ContentView: View {
    @State private var showingModal = false
    var body: some View {
        Button(action: {
            self.showingModal = true
        }) {
            Text("Show detail")
        }.sheet(isPresented: $showingModal) {
            DetailView(showingModal: self.$showingModal)
        }
    }
}
like image 594
keegan3d Avatar asked Jun 07 '26 08:06

keegan3d


2 Answers

Beta 6

Use presentationMode from the @Environment.

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

    var body: some View {
        VStack {
            Text("Ohay!")
            Button("Close") {
                self.presentationMode.wrappedValue.dismiss()
            }
        }
    }
}
like image 163
backslash-f Avatar answered Jun 10 '26 06:06

backslash-f


Another solution is to add a delegate property to your SwifUI view which passes the dismiss action back to the presenter.

protocol MySwiftUIViewDelegate: class {
    func myDismissAction()
}

struct MySwiftUIView {
    weak var delegate: MySwiftUIViewDelegate?
    
    var body: some View {
        Button("Dismiss") {
            self.delegate?.myDismissAction()
        }
    }
}

class MyViewController: UIViewController, MySwiftUIViewDelegate {
    func presentMyView() {
        var myView = MySwiftUIView()
        myView.delegate = self

        let hostingViewController = UIHostingController(rootView: myView)
        present(vc, animated: true, completion: nil)
    }

    // MARK: - MySwiftUIViewDelegate

    func myDismissAction() {
        dismiss(animated: true)
    }
}

While this may seem a bit convoluted, it is also arguably better to make the presenter responsible for handling the dismissal, so that the view doesn't have to know how it was presented (e.g. push vs modal), thus making your code more modular. Plus you might want other delegated methods, depending on what you're working on, so you might have a delegate protocol already. And it gives you a convenient place to execute any additional code when the view is dismissed.

(Although keep in mind that depending on the modal presentation style / settings, users may also be able to dismiss by pulling the view down.)

like image 23
shim Avatar answered Jun 10 '26 06:06

shim