I have a SwiftUI view wrapped in UIHostingController
and presented modally as formSheet
on iPad. I have a done button to dismiss and have a call back closure passed from parent view controller to perform actions when this done button is pressed. The problem happens on iPad where the user may tap outside of the view and the view gets dismissed thereby missing the callback.
One way to solve it would be to trigger the callback closure in onDismiss
of the SwiftUI view. While it works, I am not sure if it is the right place and if it is reliable enough. The documentation only says that onDismiss
is called when the "view disappears from the screen". However, what if we push a view on top of this view in a navigation hierarchy, will onDismiss
be still called?
From your description, I assume you have a SwiftUI view like this:
struct SomeView: View {
let callback: () -> Void
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationStack {
Text("Foo")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
callback()
dismiss()
}
}
}
}
}
}
When presenting the hosting controller, you can set its presentationController
's delegate and implement the delegate method presentationControllerDidDismiss
. For example:
@objc func somethingIsClicked() {
let host = UIHostingController(rootView: SomeView(callback: callback))
host.modalPresentationStyle = .formSheet
host.presentationController?.delegate = self
present(host, animated: true)
}
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
callback()
}
func callback() {
print("Dismissed!")
}
presentationControllerDidDismiss
will be called when the sheet is completely dismissed, not when the user "starts" to dismiss it (which can be detected with presentationControllerWillDismiss
), because at this point the user can change their mind and stop dismissing.
As its documentation says, presentationControllerDidDismiss
isn't called when the sheet is dismissed programmatically, so you still need to pass the callback to the SwiftUI view.
Also note that you can set:
host.isModalInPresentation = true
to force the user to dismiss the sheet using your done button.