Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
SwiftUI - prevent auto dismiss the sheet by drag down
//
// Created by https://quickplan.app on 2020/11/8.
//
import SwiftUI
/// Control if allow to dismiss the sheet by the user actions
/// - Drag down on the sheet on iPhone and iPad
/// - Tap outside the sheet on iPad
/// No impact to dismiss programatically (by calling "presentationMode.wrappedValue.dismiss()")
/// -----------------
/// Tested on iOS 14.2 with Xcode 12.2 RC
/// This solution may NOT work in the furture.
/// -----------------
struct MbModalHackView: UIViewControllerRepresentable {
var dismissable: () -> Bool = { false }
func makeUIViewController(context: UIViewControllerRepresentableContext<MbModalHackView>) -> UIViewController {
MbModalViewController(dismissable: self.dismissable)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
extension MbModalHackView {
private final class MbModalViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
let dismissable: () -> Bool
init(dismissable: @escaping () -> Bool) {
self.dismissable = dismissable
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
setup()
}
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
dismissable()
}
// set delegate to the presentation of the root parent
private func setup() {
guard let rootPresentationViewController = self.rootParent.presentationController, rootPresentationViewController.delegate == nil else { return }
rootPresentationViewController.delegate = self
}
}
}
extension UIViewController {
fileprivate var rootParent: UIViewController {
if let parent = self.parent {
return parent.rootParent
}
else {
return self
}
}
}
/// make the call the SwiftUI style:
/// view.allowAutDismiss(...)
extension View {
/// Control if allow to dismiss the sheet by the user actions
public func allowAutoDismiss(_ dismissable: @escaping () -> Bool) -> some View {
self
.background(MbModalHackView(dismissable: dismissable))
}
/// Control if allow to dismiss the sheet by the user actions
public func allowAutoDismiss(_ dismissable: Bool) -> some View {
self
.background(MbModalHackView(dismissable: { dismissable }))
}
}
/// 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)
}
}
}
struct ModalContent: View {
@Environment(\.presentationMode) private var presentationMode
var body: some View {
VStack {
Text("Hello")
.padding()
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Text("Dismiss")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@shliama

This comment has been minimized.

Copy link

@shliama shliama commented Oct 14, 2020

Awesome! Works on macOS too 🙌

@USBA

This comment has been minimized.

Copy link

@USBA USBA commented Oct 26, 2020

Thanks sooo much

@USBA

This comment has been minimized.

Copy link

@USBA USBA commented Nov 8, 2020

Hey @mobilinked, this worked completely fine on Xcode 12.1 and iOS. 14.1. But now, it doesn't work on Xcode 12.2 RC and iOS. 14.2.
I have no idea whether it is a bug in Xcode 12.2 RC or this just doesn't work on iOS 14.2. Please check on this 🙏

@mobilinked

This comment has been minimized.

Copy link
Owner Author

@mobilinked mobilinked commented Nov 8, 2020

Hi @USBA, I checked and find the cause. I will update it in these two days (If the new solution in my mind works).

BTW, this code is just a temporary solution as SwiftUI lacks the functionality now, and I believe SwiftUI will provide the API in the future. Using this code, the tough case is that it's hard to know if Apple will change something next version to make this code not work again.

@USBA

This comment has been minimized.

Copy link

@USBA USBA commented Nov 8, 2020

That's great. Hope it'll work. Thanks

@mobilinked

This comment has been minimized.

Copy link
Owner Author

@mobilinked mobilinked commented Nov 8, 2020

Hi @USBA, updated.

@USBA

This comment has been minimized.

Copy link

@USBA USBA commented Nov 9, 2020

Thanks @mobilinked, you're a lifesaver.

@YanSteph

This comment has been minimized.

Copy link

@YanSteph YanSteph commented Nov 20, 2020

Thanks !

@slavabily

This comment has been minimized.

Copy link

@slavabily slavabily commented Jul 14, 2021

Brilliant solution! Thanks so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment