Skip to content

Instantly share code, notes, and snippets.

@mobilinked
Last active February 9, 2024 13:03
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mobilinked/9b6086b3760bcf1e5432932dad0813c0 to your computer and use it in GitHub Desktop.
Save mobilinked/9b6086b3760bcf1e5432932dad0813c0 to your computer and use it in GitHub Desktop.
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
Copy link

shliama commented Oct 14, 2020

Awesome! Works on macOS too 🙌

@USBA
Copy link

USBA commented Oct 26, 2020

Thanks sooo much

@USBA
Copy link

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
Copy link
Author

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
Copy link

USBA commented Nov 8, 2020

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

@mobilinked
Copy link
Author

Hi @USBA, updated.

@USBA
Copy link

USBA commented Nov 9, 2020

Thanks @mobilinked, you're a lifesaver.

@YanSte
Copy link

YanSte commented Nov 20, 2020

Thanks !

@slavabily
Copy link

Brilliant solution! Thanks so much!

@AntonBrock
Copy link

Thank you!

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