What about extend environment values with hosting controller presenter? It allows to be used like presentationMode, from any view in the hierarchy and it is easily reusable and scalable.
Define your new environment value:
struct UIHostingControllerPresenter {
init(_ hostingControllerPresenter: UIViewController) {
self.hostingControllerPresenter = hostingControllerPresenter
}
private unowned var hostingControllerPresenter: UIViewController
func dismiss() {
if let presentedViewController = hostingControllerPresenter.presentedViewController, !presentedViewController.isBeingDismissed { // otherwise an ancestor dismisses hostingControllerPresenter - which we don't want.
hostingControllerPresenter.dismiss(animated: true, completion: nil)
}
}
}
private enum UIHostingControllerPresenterEnvironmentKey: EnvironmentKey {
static let defaultValue: UIHostingControllerPresenter? = nil
}
extension EnvironmentValues {
/// An environment value that attempts to extend `presentationMode` for case where
/// view is presented via `UIHostingController` so dismissal through
/// `presentationMode` doesn't work.
var uiHostingControllerPresenter: UIHostingControllerPresenter? {
get { self[UIHostingControllerPresenterEnvironmentKey.self] }
set { self[UIHostingControllerPresenterEnvironmentKey.self] = newValue }
}
}
Then pass the value when needed like:
let view = AnySwiftUIView().environment(\.uiHostingControllerPresenter, UIHostingControllerPresenter(self))
let viewController = UIHostingController(rootView: view)
present(viewController, animated: true, completion: nil)
...
And enjoy using
@Environment(\.uiHostingControllerPresenter) private var uiHostingControllerPresenter
...
uiHostingControllerPresenter?.dismiss()
where you otherwise go with
@Environment(\.presentationMode) private var presentationMode
...
presentationMode.wrappedValue.dismiss() // .isPresented = false