So far, we have learned how to present UIViewControllers
modally by starting ModalChild
and NavigationChild
instances. These classes allow you to define the context in which the view controller will be presented. It's guaranteed to be presented in the exact way that you expect.
However, there is a way to create a coordinator that adapts to the presentation context of the parent coordinator. You use AdaptiveCoordinator
to achieve this goal:
class PaymentMethodCoordinator: AdaptiveCoordinator<PaymentMethodScreenResult> {
override var viewController: UIViewController {
let viewController = PaymentMethodViewController()
return viewController
}
}
ModalCoordinator
and NavigationCoordinator
both have self.navigation
. For both classes, this object provides a very specialized interface to start new instances of both ModalCoordinator
and NavigationCoordinator
.
Additionally, self.navigation
always conforms to AdaptiveNavigation
protocol. This interface enables you to start AdaptiveCoordinator
s.
Here is the start
method available on AdaptiveNavigation
:
public struct AdaptiveContext {
public static var current: AdaptiveContext
public static var modal: AdaptiveContext
public static func navigation(new navigationController: UINavigationController) -> AdaptiveContext
public static func navigation(fallback navigationController: @autoclosure @escaping () -> UINavigationController) -> AdaptiveContext
}
public protocol AdaptiveNavigation {
func start<NewResult>(_ child: AdaptiveCoordinator<NewResult>,
in context: AdaptiveContext,
animated: Bool,
_ completion: @escaping (NewResult?) -> Void)
}
Notice the context parameter. This parameter allows to control the way the child will be presented on screen.
Starting with this context means that the child will be presented just like its parent:
Parent is presented | Child will be presented |
---|---|
Via present |
Via present |
Via push |
Via push |
"Current" stands for "current presentation context"
This overrides the current context and enforces modal presentation:
Parent is presented | Child will be presented |
---|---|
Via present |
Via present |
Via push |
Via present |
This overrides the current context and starts a new UINavigationController
Parent is presented | Child will be presented |
---|---|
Via present |
as a root of the given UINavigationController . The navigation controller will be presented modally on top of modally presented parent. |
Via push |
as a root of the given UINavigationController . The navigation controller will be presented modally on top of the existing UINavigationController . |
This kind of context allows you to push
if there is already a UINavigationController
and to start a new UINavigationController
if the parent didn't provide any.
This method accepts an @autoclosure
which is not always evaluated.
Current Presentation Context | Outcome |
---|---|
Already a UINavigationController |
It pushes the child unto the existing UINavigationController . fallback -autoclosure parameter never gets evaluated. |
Modal presentation | It creates the UINavigationController from the @autoclosure that you pass as fallback parameter. Then, it starts child as the rootViewController of the UINavigationController . |
⚠️ Be careful not to create the UINavigationController ahead of passing it into autoclosure
Do this:
self.navigation.start(coordinator, in: .navigation(fallback: UINavigationController()), animated: true) { _ in }
Not this:
let navigationController = UINavigationController() // Already created
self.navigation.start(coordinator, in: .navigation(fallback: navigationController), animated: true) { _ in }
self.navigation
also provides 2 methods that allow you to return to the realm of ModalCoordinator
and NavigationCoordinator
.
public protocol AdaptiveNavigation {
func start<NewResult>(_ child: ModalCoordinator<NewResult>,
animated: Bool,
_ completion: @escaping (NewResult?) -> Void)
func start<NewResult>(_ navigationController: UINavigationController,
_ child: NavigationCoordinator<NewResult>,
animated: Bool,
_ completion: @escaping (NewResult?) -> Void)
}
Of course, these instances of ModalCoordinator
and NavigationCoordinator
will see their proper navigation interfaces in self.navigation
- the ModalNavigation
and NavigationControllerNavigation
.