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