Created
September 21, 2023 14:17
-
-
Save phillipcaudell/c7135ec8d7933e391fc6223448a6af0a to your computer and use it in GitHub Desktop.
A presentation style that displays the content centered in the screen smaller than a regular sheet.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
extension View { | |
/// Presents a form sheet when a binding to a Boolean value that you provide is true. | |
/// - Parameters: | |
/// - isPresented: A binding to a Boolean value that determines whether | |
/// to present the sheet that you create in the modifier's | |
/// `content` closure. | |
/// - interactiveDismiss: A Boolean value that indicates whether to | |
/// prevent nonprogrammatic dismissal. | |
/// - content: A closure that returns the content of the sheet. | |
public func formSheet<Content>( | |
isPresented: Binding<Bool>, | |
interactiveDismiss: Bool = true, | |
@ViewBuilder content: @escaping () -> Content | |
) -> some View where Content : View { | |
self.background { | |
FormSheetPresentable( | |
isPresenting: isPresented, | |
interactiveDismiss: interactiveDismiss, | |
content: content | |
) | |
} | |
} | |
} | |
fileprivate struct FormSheetPresentable<Content>: UIViewControllerRepresentable where Content : View { | |
@Binding var isPresenting: Bool | |
let interactiveDismiss: Bool | |
@ViewBuilder let content: () -> Content | |
@State private var contentViewController: UIViewController? | |
func makeUIViewController(context: Context) -> some UIViewController { | |
UIViewController() | |
} | |
func updateUIViewController(_ viewController: UIViewControllerType, context: Context) { | |
if isPresenting { | |
guard self.contentViewController == nil else { | |
return | |
} | |
let contentViewController = makeContentViewController() | |
contentViewController.presentationController?.delegate = context.coordinator | |
viewController.present(contentViewController, animated: true) | |
contentViewController.isModalInPresentation = !interactiveDismiss | |
setContentViewController(contentViewController) | |
} else { | |
guard let contentViewController else { | |
return | |
} | |
contentViewController.dismiss(animated: true) | |
setContentViewController(nil) | |
} | |
} | |
func setContentViewController(_ viewController: UIViewController?) { | |
DispatchQueue.main.async { | |
self.contentViewController = viewController | |
} | |
} | |
func makeContentViewController() -> UIViewController { | |
let viewController = UIHostingController(rootView: content()) | |
viewController.modalPresentationStyle = .formSheet | |
return viewController | |
} | |
func makeCoordinator() -> Coordinator { | |
Coordinator(self) | |
} | |
} | |
extension FormSheetPresentable { | |
class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate { | |
let parent: FormSheetPresentable | |
init(_ parent: FormSheetPresentable) { | |
self.parent = parent | |
} | |
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { | |
self.parent.isPresenting = false | |
self.parent.contentViewController = nil | |
} | |
} | |
} | |
struct FormSheetPreview: View { | |
@State private var show = false | |
var body: some View { | |
Button { | |
show.toggle() | |
} label: { | |
Text("\(show ? "Hide" : "Show") Sheet") | |
} | |
.formSheet(isPresented: $show) { | |
Text("Hello there") | |
} | |
} | |
} | |
#Preview { | |
FormSheetPreview() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment