SwiftUI 2.0 presented on WWDC20 brings us a lot of small improvements and additions to the APIs. In iOS 13 we already had API to open views modally in a bottom-rising sheet, but whatever the contents presentation mode was, there was no way to present it full screen.Real-life example: early versions of our app had to present Camera of UIImagePickerController in such sheet which looked very stupid.
One of the new APIs adds an ability to open views modally - fullScreenCover(isPresented:onDismiss:content:).Unfortunately, this view modifier is available only on iOS 14 and higher.
How can it be back ported to iOS 13?
Short answer: with help of UIKit and thanks to its interoperability with SwiftUI.
As one can remember, UIKit is managing view hierarchy by means of UIViewControllers which can serve as presenters to each other. In a simple cases presentation transition (animation and which part of the screen the presented controller's view will take) depends on presenting controller's .presentationStyle;
SwiftUI can wrap UIViewControllers into UIViewControllerRepresentable views and embed into hierarchy like any other View. Such controllers have 2 important methods (makeUIViewController, dismantleUIViewController) which will help us manage presentation process
So, generally the plan is:
Add invisible UIViewController to parent SwiftUI view
Provide this controller with a child SwitUI view that needs to be presented in a full screen modal
When this controller appears in the hierarchy, it will present the child using UIKit APIs
When the child should be dismissed, the controller should be removed from the hierarchy
Empowered by the beauty of ViewModifiers we can keep code almost the same!
Production code will be more complex, but for the sake of simplicity let's omit edge cases:
First, we'll create ModalContainerModifier which will make our API indistinguishable from native SwiftUI's one. It will embed invisible ModalContainer (UIViewControllerRepresentable) to a background when modal should be presented;
In makeUIViewController we'll be wrapping SwiftUI contents-to-be-presented into UIHostingController and define presentationStyle of this child view controller - in this case .overFullScreen - which later UIKit will use for presentation. The invisible proxy will be a blank UIViewController, it's purpose is to call present(_:animated:completion) once it appears;
Another important point is dismantleUIViewController - it will be called once SwiftUI removes our ModalContainer from the hierarchy. That is a correct moment to dismiss child viewController, otherwise it will never ever be removed.