Using UISheetPresentationController in SwiftUI
If you want the Image Picker
import SwiftUI
///Sample usage
@available(iOS 15.0, macCatalyst 15.0,*)
struct ImagePickerParentView: View {
@State var isPresented = false
@State var selectedImage: UIImage? = nil
var body: some View {
print("ImagePickerParentView :: \(#function) :: isPresented == \(isPresented)")
return VStack{
if selectedImage != nil{
Image(uiImage: selectedImage!)
.resizable()
.frame(width: 100, height: 100)
}
Button("present image picker", action: {
isPresented.toggle()
}).imagePicker(isPresented: $isPresented, uiImage: $selectedImage, detents: [.medium()], largestUndimmedDetentIdentifier: .large)
}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
extension View {
func imagePicker(isPresented: Binding<Bool>, uiImage: Binding<UIImage?>, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, prefersGrabberVisible: Bool = false, preferredCornerRadius: CGFloat? = nil)-> some View {
print("\(#function) :: isPresented == \(isPresented)")
return modifier(ImagePickerViewModifier(isPresented: isPresented, uiImage: uiImage, detents : detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, preferredCornerRadius: preferredCornerRadius))
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct ImagePickerViewModifier: ViewModifier {
@Binding var isPresented: Bool
@Binding var uiImage: UIImage?
let detents : [UISheetPresentationController.Detent]
let largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
let prefersScrollingExpandsWhenScrolledToEdge: Bool
let prefersEdgeAttachedInCompactHeight: Bool
let prefersGrabberVisible: Bool
let preferredCornerRadius: CGFloat?
func body(content: Content) -> some View {
print("ImagePickerViewModifier :: \(#function) :: isPresented == \(isPresented)")
return content.overlay(
AdaptiveImagePicker_UI(isPresented: $isPresented, uiImage: $uiImage, detents: detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, preferredCornerRadius: preferredCornerRadius).frame(width: 0, height: 0)
)
.onChange(of: isPresented, perform: { value in
print("AdaptiveSheet :: onChange :: isPresented == \(value)")
})
//}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct AdaptiveImagePicker_UI: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@Binding var uiImage: UIImage?
var detents : [UISheetPresentationController.Detent] = [.medium(), .large()]
var largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium
var prefersScrollingExpandsWhenScrolledToEdge: Bool = false
var prefersEdgeAttachedInCompactHeight: Bool = true
var prefersGrabberVisible: Bool = false
var preferredCornerRadius: CGFloat?
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> AdaptiveImagePickerViewController {
print("CustomSheet_UI :: \(#function) :: isPresented == \(isPresented)")
let vc = AdaptiveImagePickerViewController(coordinator: context.coordinator, detents : detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, preferredCornerRadius: preferredCornerRadius)
return vc
}
func updateUIViewController(_ uiViewController: AdaptiveImagePickerViewController, context: Context) {
print("CustomSheet_UI :: \(#function) :: isPresented == \(isPresented)")
print("CustomSheet_UI :: \(#function) :: context.coordinator.parent.isPresented == \(context.coordinator.parent.isPresented)")
if isPresented {
uiViewController.presentImagePicker()
}else{
uiViewController.dismissModalView()
}
}
class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: AdaptiveImagePicker_UI
var isPresented: Bool = false
init(_ parent: AdaptiveImagePicker_UI) {
print("CustomSheet_UI :: \(#function) :: parent.isPresented == \(parent.isPresented)")
self.parent = parent
}
//Adjust the variable when the user dismisses with a swipe
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("CustomSheet_UI.Coordinator :: \(#function) :: parent.isPresented == \(parent.isPresented)")
if parent.isPresented{
parent.isPresented = false
}
}
//Adjust the variable when the user cancels
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
if parent.isPresented{
parent.isPresented = false
}
}
//Get access to the selected image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let image = info[.originalImage] as? UIImage {
parent.uiImage = image
parent.isPresented = false
}
}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
class AdaptiveImagePickerViewController: UIViewController {
var coordinator: AdaptiveImagePicker_UI.Coordinator
let detents : [UISheetPresentationController.Detent]
let largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
let prefersScrollingExpandsWhenScrolledToEdge: Bool
let prefersEdgeAttachedInCompactHeight: Bool
let prefersGrabberVisible: Bool
let preferredCornerRadius: CGFloat?
private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
init(coordinator: AdaptiveImagePicker_UI.Coordinator, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, prefersGrabberVisible: Bool, preferredCornerRadius: CGFloat?) {
print("AdaptiveImagePickerViewController :: \(#function) :: isPresented == \(coordinator.parent.isPresented)")
self.coordinator = coordinator
self.detents = detents
self.largestUndimmedDetentIdentifier = largestUndimmedDetentIdentifier
self.prefersEdgeAttachedInCompactHeight = prefersEdgeAttachedInCompactHeight
self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
self.prefersGrabberVisible = prefersGrabberVisible
self.preferredCornerRadius = preferredCornerRadius
super.init(nibName: nil, bundle: .main)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func dismissModalView(){
print("AdaptiveImagePickerViewController :: \(#function) :: isPresented == \(coordinator.parent.isPresented)")
dismiss(animated: true, completion: nil)
}
//This is mostly code from the Apple sample
//https://developer.apple.com/documentation/uikit/uiviewcontroller/customize_and_resize_sheets_in_uikit
func presentImagePicker(){
guard presentedViewController == nil else {
dismiss(animated: true, completion: {
self.presentImagePicker()
})
return
}
let imagePicker = UIImagePickerController()
imagePicker.delegate = coordinator
imagePicker.modalPresentationStyle = .popover
//Added the presentation controller delegate to detect if the user swipes to dismiss
imagePicker.presentationController?.delegate = coordinator as UIAdaptivePresentationControllerDelegate
if let hostPopover = imagePicker.popoverPresentationController {
hostPopover.sourceView = super.view
let sheet = hostPopover.adaptiveSheetPresentationController
//As of 13 Beta 4 if .medium() is the only detent in landscape error occurs
sheet.detents = (isLandscape ? [.large()] : detents)
sheet.largestUndimmedDetentIdentifier =
largestUndimmedDetentIdentifier
sheet.prefersScrollingExpandsWhenScrolledToEdge =
prefersScrollingExpandsWhenScrolledToEdge
sheet.prefersEdgeAttachedInCompactHeight =
prefersEdgeAttachedInCompactHeight
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
sheet.prefersGrabberVisible = prefersGrabberVisible
sheet.preferredCornerRadius = preferredCornerRadius
}
present(imagePicker, animated: true, completion: nil)
}
/// To compensate for l orientation
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
isLandscape = true
self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = [.large()]
} else {
isLandscape = false
self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = detents
}
super.viewWillTransition(to: size, with: coordinator)
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct ImagePickerParentView_Previews: PreviewProvider {
static var previews: some View {
ImagePickerParentView()
}
}
If you want one that takes any SwiftUI View
it only needs a few changes.
//This is the sample usage
@available(iOS 15.0, macCatalyst 15.0,*)
struct CustomSheetParentView: View {
@State var isPresented = false
var body: some View {
print("CustomSheetParentView :: \(#function) :: isPresented == \(isPresented)")
return VStack{
Button("present sheet", action: {
isPresented.toggle()
}).adaptiveSheet(isPresented: $isPresented, detents: [.medium()], largestUndimmedDetentIdentifier: .medium, disableSwipeToDismiss: false){
Rectangle()
.frame(maxWidth: .infinity, maxHeight: 100, alignment: .center)
.foregroundColor(.clear)
.border(Color.blue, width: 3)
.overlay(
LazyVStack{
Text("Hello, World!")
Button("dismiss", action: {
print("dismiss button :: isPresented == \(isPresented)")
isPresented = false
})
CustomSheetParentView()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onTapGesture {
print("onTap :: isPresented == \(isPresented)")
isPresented.toggle()
}
)
.background(Color(UIColor.systemBackground))
}
}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct CustomSheetView_Previews: PreviewProvider {
static var previews: some View {
CustomSheetParentView()
}
}
//EVERYTHING from here down is Reusable and can be pasted into a project and then use `.adaptiveSheet` just like `.sheet`
@available(iOS 15.0, macCatalyst 15.0,*)
extension View {
func adaptiveSheet<T: View>(isPresented: Binding<Bool>, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, prefersGrabberVisible: Bool = false, disableSwipeToDismiss: Bool = false, preferredCornerRadius: CGFloat? = nil, @ViewBuilder content: @escaping () -> T)-> some View {
print("\(#function) :: isPresented == \(isPresented)")
return modifier(AdaptiveSheet<T>(isPresented: isPresented, detents : detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, disableSwipeToDismiss: disableSwipeToDismiss, preferredCornerRadius: preferredCornerRadius, sheetContent: content))
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct AdaptiveSheet<T: View>: ViewModifier {
@Binding var isPresented: Bool
let detents : [UISheetPresentationController.Detent]
let largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
let prefersScrollingExpandsWhenScrolledToEdge: Bool
let prefersEdgeAttachedInCompactHeight: Bool
let prefersGrabberVisible: Bool
let disableSwipeToDismiss: Bool
let preferredCornerRadius: CGFloat?
@ViewBuilder let sheetContent: T
func body(content: Content) -> some View {
print("AdaptiveSheet :: \(#function) :: isPresented == \(isPresented)")
return content.overlay(
CustomSheet_UI(isPresented: $isPresented, detents: detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, disableSwipeToDismiss: disableSwipeToDismiss,preferredCornerRadius: preferredCornerRadius, content: {sheetContent}).frame(width: 0, height: 0)
)
.onChange(of: isPresented, perform: { value in
print("AdaptiveSheet :: onChange :: isPresented == \(value)")
})
//}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
struct CustomSheet_UI<T: View>: UIViewControllerRepresentable {
@Binding var isPresented: Bool
var detents : [UISheetPresentationController.Detent] = [.medium(), .large()]
var largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium
var prefersScrollingExpandsWhenScrolledToEdge: Bool = false
var prefersEdgeAttachedInCompactHeight: Bool = true
var prefersGrabberVisible: Bool = false
var disableSwipeToDismiss: Bool = false
var preferredCornerRadius: CGFloat?
@ViewBuilder let content: T
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> CustomSheetViewController<T> {
print("CustomSheet_UI :: \(#function) :: isPresented == \(isPresented)")
let vc = CustomSheetViewController(coordinator: context.coordinator, detents : detents, largestUndimmedDetentIdentifier: largestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, prefersGrabberVisible: prefersGrabberVisible, disableSwipeToDismiss: disableSwipeToDismiss, preferredCornerRadius: preferredCornerRadius, content: {content})
return vc
}
func updateUIViewController(_ uiViewController: CustomSheetViewController<T>, context: Context) {
print("CustomSheet_UI :: \(#function) :: isPresented == \(isPresented)")
print("CustomSheet_UI :: \(#function) :: context.coordinator.parent.isPresented == \(context.coordinator.parent.isPresented)")
if isPresented {
uiViewController.presentModalView()
}else{
uiViewController.dismissModalView()
}
}
class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
var parent: CustomSheet_UI
var isPresented: Bool = false
init(_ parent: CustomSheet_UI) {
print("CustomSheet_UI :: \(#function) :: parent.isPresented == \(parent.isPresented)")
self.parent = parent
}
//Adjust the variable when the user dismisses with a swipe
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("CustomSheet_UI.Coordinator :: \(#function) :: parent.isPresented == \(parent.isPresented)")
if parent.isPresented{
parent.isPresented = false
}
}
}
}
@available(iOS 15.0, macCatalyst 15.0,*)
class CustomSheetViewController<Content: View>: UIViewController {
let content: Content
var coordinator: CustomSheet_UI<Content>.Coordinator
let detents : [UISheetPresentationController.Detent]
let largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
let prefersScrollingExpandsWhenScrolledToEdge: Bool
let prefersEdgeAttachedInCompactHeight: Bool
let prefersGrabberVisible: Bool
let disableSwipeToDismiss: Bool
let preferredCornerRadius: CGFloat?
private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
init(coordinator: CustomSheet_UI<Content>.Coordinator, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], largestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, prefersGrabberVisible: Bool, disableSwipeToDismiss: Bool, preferredCornerRadius: CGFloat?, @ViewBuilder content: @escaping () -> Content) {
print("CustomSheetViewController :: \(#function) :: isPresented == \(coordinator.parent.isPresented)")
self.content = content()
self.coordinator = coordinator
self.detents = detents
self.largestUndimmedDetentIdentifier = largestUndimmedDetentIdentifier
self.prefersEdgeAttachedInCompactHeight = prefersEdgeAttachedInCompactHeight
self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
self.prefersGrabberVisible = prefersGrabberVisible
self.disableSwipeToDismiss = disableSwipeToDismiss
self.preferredCornerRadius = preferredCornerRadius
super.init(nibName: nil, bundle: .main)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func dismissModalView(){
print("CustomSheetViewController :: \(#function) :: isPresented == \(coordinator.parent.isPresented)")
dismiss(animated: true, completion: nil)
}
func presentModalView(){
print("CustomSheetViewController :: \(#function) :: isPresented == \(coordinator.parent.isPresented)")
let hostingController = UIHostingController(rootView: content)
//allows background color to be decided by SwiftUI content.
// Incase you want to use a Material that gives transparency
hostingController.view.backgroundColor = nil
hostingController.modalPresentationStyle = .popover
hostingController.presentationController?.delegate = coordinator as UIAdaptivePresentationControllerDelegate
hostingController.modalTransitionStyle = .coverVertical
hostingController.isModalInPresentation = disableSwipeToDismiss
if let hostPopover = hostingController.popoverPresentationController {
hostPopover.sourceView = super.view
let sheet = hostPopover.adaptiveSheetPresentationController
//As of 13 Beta 4 if .medium() is the only detent in landscape error occurs
sheet.detents = (isLandscape ? [.large()] : detents)
sheet.largestUndimmedDetentIdentifier =
largestUndimmedDetentIdentifier
sheet.prefersScrollingExpandsWhenScrolledToEdge =
prefersScrollingExpandsWhenScrolledToEdge
sheet.prefersEdgeAttachedInCompactHeight =
prefersEdgeAttachedInCompactHeight
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
sheet.prefersGrabberVisible = prefersGrabberVisible
sheet.preferredCornerRadius = preferredCornerRadius
}
if presentedViewController == nil{
present(hostingController, animated: true, completion: nil)
}
}
/// To compensate for l orientation
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
isLandscape = true
self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = [.large()]
} else {
isLandscape = false
self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = detents
}
super.viewWillTransition(to: size, with: coordinator)
}
}
How to present a modal view controller like with UIKit's UIViewControllerTransitioningDelegate?
Well there is no built-in such flexibility say with standard .sheet, but it can be implemented custom very fast.
Here is simple demo (Xcode 13.3 / iOS 15.4)
Main part:
struct ElementsList: View {
// ...
ModalView(isPresented: $isModal) {
List(elements, id: \.self) {
struct ModalView<V: View>: View {
@Binding var isPresented: Bool
// ...
ZStack {
content()
ZStack {
VStack {
if isPresented {
Color.black.opacity(0.8)
.transition(.opacity)
}
}.animation(.easeInOut(duration: 0.25), value: isPresented)
Complete test code in project is here
Set UIWindowScene.PresentationStyle on Scene?
I've reached out to Apple via a TSI about this particular issue. As of the current release of SwiftUI, this isn't possible. They recommend I use the .sheet
modifier instead and drop the split window capability. Otherwise, I can create a UISceneDelegate and use UIKit APIs directly:
let options = UIWindowScene.ActivationRequestOptions()
options.preferredPresentationStyle = .prominent
let userActivity = NSUserActivity(activityType: SecondSceneDelegate.sceneUserActivityType)
userActivity.targetContentIdentifier = SecondSceneDelegate.sceneUserActivityType
UIApplication.shared.requestSceneSessionActivation(nil,
userActivity: userActivity,
options: options,
errorHandler: nil)
SwiftUI - Half modal?
iOS 16+
It looks like half sheet is finally supported in iOS 16.
To manage the size of sheet we can use PresentationDetent
and specifically presentationDetents(_:selection:)
Here's an example from the documentation:
struct ContentView: View {
@State private var showSettings = false
@State private var settingsDetent = PresentationDetent.medium
var body: some View {
Button("View Settings") {
showSettings = true
}
.sheet(isPresented: $showSettings) {
SettingsView()
.presentationDetents:(
[.medium, .large],
selection: $settingsDetent
)
}
}
}
Note that if you provide more that one detent, people can drag the sheet to resize it.
Here are possible values for PresentationDetent:
large
medium
fraction(CGFloat)
height(CGFloat)
custom<D>(D.Type)
SwiftUI Custom Half Modal Calculator Not Working
The problem is in your func halfSheet()
and struct HalfSheetHelper
. You have created a UIViewControllerRepresentable view, but it does not handle updates.
Once the sheet has already been shown, the sequence of events happening is:
- Pressing a button triggers a change to
self.value
insidePGQuestion1
. - SwiftUI re-renders
PGQuestion1
, and a new closure is passed to.halfSheet { ... }
which uses the newvalue
. - Since the
HalfSheetHelper
isUIViewRepresentable
, SwiftUI calls yourupdateUIViewController()
function. A completely new CustomHostingController is created. Then nothing else happens becauseshowSheet
isfalse
.
To handle updates properly, I recommend you create a Coordinator class inside your HalfSheetHelper. The coordinator is a class
that SwiftUI will keep alive as long as the view is being used, and it can maintain a persistent reference to the CustomHostingController. Then in updateUIViewController()
, you can use the coordinator to access the hosting controller and re-assign its rootView
with the newly updated sheet contents. For more on these techniques, see the Interfacing with UIKit tutorial.
I also changed how showSheet
is handled so that it becomes false only once the sheet is dismissed. This required adding a custom onDismiss
closure to the CustomHostingController which it calls in viewDidDisappear
.
(There still seems to be a bug with the half-sheet, where if I swipe down to dismiss the sheet and then show it again, it appears fullscreen instead of half-screen. I'm not familiar enough with the presentationController
/detents
APIs to figure out why this is happening!)
struct HalfSheetHelper<SheetView: View>: UIViewControllerRepresentable {
var sheetView: SheetView
@Binding var showSheet: Bool
class Coordinator {
let dummyController = UIViewController()
let sheetController: CustomHostingController<SheetView>
init(sheetView: SheetView, showSheet: Binding<Bool>) {
sheetController = CustomHostingController(rootView: sheetView, onDismiss: { showSheet.wrappedValue = false })
dummyController.view.backgroundColor = .clear
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(sheetView: sheetView, showSheet: $showSheet)
}
func makeUIViewController(context: Context) -> UIViewController {
return context.coordinator.dummyController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
context.coordinator.sheetController.rootView = sheetView
if showSheet && uiViewController.presentedViewController == nil {
uiViewController.present(context.coordinator.sheetController, animated: true)
}
}
}
class CustomHostingController<Content: View>: UIHostingController<Content> {
var onDismiss: (() -> Void)?
convenience init(rootView: Content, onDismiss: @escaping () -> Void) {
self.init(rootView: rootView)
self.onDismiss = onDismiss
}
override func viewDidLoad() {
if let presentationController = presentationController as? UISheetPresentationController {
presentationController.detents = [
.medium(),
.large()
]
presentationController.prefersGrabberVisible = true
}
}
override func viewDidDisappear(_ animated: Bool) {
onDismiss?()
}
}
Related Topics
iOS App Getting Throttled from Local Searches
Xcode Creates Wrong IPA Folder Structure
Swift - Add Gesture Recognizer to Object in Table Cell
Game Center Login Dialog Not Shown Again After Cancelling It for the First Time (Ios7)
How to Properly Send an Image to Cloudkit as Ckasset
Error Using Swift - Instance Member Cannot Be Used on Type 'Viewcontroller'
Swiftui Foreach Index Out of Range Error When Removing Row
Swift Framework Does Not Include Symbols from Extensions to Generic Structs
iOS 11 Animated Gif Display in Uiimageview
Swiftui: How to Get Continuous Updates from Slider
Remove Uiwebview's Internal Cache
Pass Uicollectionview Touch Event to Its Parent Uitableviewcell
Autolayout: Origin and Size Should Change According to Width and Height Factor
Understanding Model-View-Controller
Uiwebview Not Go to Didfailloadwitherror When Weblink Not Found
How to Create a Uiimage with Uibezierpath