28
votes

I've presented a modal view but I would like the user to go through some steps before it can be dismissed. Currently the view can be dragged to dismiss.

Is there a way to stop this from being possible?

I've watched the WWDC Session videos and they mention it but I can't seem to put my finger on the exact code I'd need.

struct OnboardingView2 : View {

    @Binding
    var dismissFlag: Bool

    var body: some View {

        VStack {
            Text("Onboarding here! ????????")
            Button(action: {
                self.dismissFlag.toggle()
            }) {
                Text("Dismiss")
            }
        }
    }
}

I currently have some text and a button I'm going to use at a later date to dismiss the view.

5
Rob, Could you please post the link of the mentioned WWDC video?Matteo Pacini
@Prcela it doesn't sounds like it - he's asking how to disable the gesture recognizerMatteo Pacini
@Prcela different question I'd say :)Chris
Have you found a solution to this?konrad.bajtyngier
@konrad.bajtyngier If you are still looking I posted a way that does not use an overlay (ZStack) at stackoverflow.com/a/60939207/6433690R. J.

5 Answers

6
votes

It is easy if you use the 3rd party lib Introspect, which is very useful as it access the corresponding UIKit component easily. In this case, the property in UIViewController:

VStack { ... }
.introspectViewController {
    $0.isModalInPresentation = true
}
6
votes

iOS 15

Starting from iOS 15 we can use interactiveDismissDisabled:

func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View

We just need to attach it to the sheet. Here is an example from the documentation:

struct PresentingView: View {
    @Binding var showTerms: Bool

    var body: some View {
        AppContents()
            .sheet(isPresented: $showTerms) {
                Sheet()
            }
    }
}

struct Sheet: View {
    @State private var acceptedTerms = false
    
    var body: some View {
        Form {
            Button("Accept Terms") {
                acceptedTerms = true
            }
        }
        .interactiveDismissDisabled(!acceptedTerms)
    }
}
1
votes

Not sure this helps or even the method to show the modal you are using but when you present a SwiftUI view from a UIViewController using UIHostingController

let vc = UIHostingController(rootView: <#your swiftUI view#>(<#your parameters #>))

you can set a modalPresentationStyle. You may have to decide which of the styles suits your needs but .currentContext prevents the dragging to dismiss.

Side note:I don't know how to dismiss a view presented from a UIHostingController though which is why I've asked a Q myself on here to find out 😂

0
votes

I had a similar question here

struct Start : View {
let destinationView = SetUp()
    .navigationBarItem(title: Text("Set Up View"), titleDisplayMode: .automatic, hidesBackButton: true)

var body: some View {
    NavigationView {
        NavigationButton(destination: destinationView) {
            Text("Set Up")

        }
    }
}
}

The main thing here is that it is hiding the back button. This turns off the back button and makes it so the user can't swipe back ether.

For the setup portion of your app you could create a new SwiftUI file and add a similar thing to get home, while also incorporating your own setup code.

struct SetUp : View {

    let destinationView = Text("Your App Here")
        .navigationBarItem(title: Text("Your all set up!"), titleDisplayMode: .automatic, hidesBackButton: true)

 var body: some View {
    NavigationView {
        NavigationButton(destination: destinationView) {
            Text("Done")

        }
    }
}
}
-2
votes

There is an extension to make controlling the modal dismission effortless, at https://gist.github.com/mobilinked/9b6086b3760bcf1e5432932dad0813c0

A temporary solution before the official solution released by Apple.

/// Example:
struct ContentView: View {
    @State private var presenting = false
    
    var body: some View {
        VStack {
            Button {
                presenting = true
            } label: {
                Text("Present")
            }
        }
        .sheet(isPresented: $presenting) {
            ModalContent()
                .allowAutoDismiss { false }
                // or
                // .allowAutoDismiss(false)
        }
    }
}