-
-
Save mobilinked/9b6086b3760bcf1e5432932dad0813c0 to your computer and use it in GitHub Desktop.
// | |
// 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() | |
} | |
} |
Thanks sooo much
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 🙏
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.
That's great. Hope it'll work. Thanks
Hi @USBA, updated.
Thanks @mobilinked, you're a lifesaver.
Thanks !
Brilliant solution! Thanks so much!
Thank you!
Awesome! Works on macOS too 🙌