我正在关注John Sundell的帖子,以实现导航器模式(https://www.swiftbysundell.com/posts/navigation-in-swift)。基本思想是,与Coordinator模式相反,每个视图控制器可以简单地调用navigator.navigate(to: .someScreen)
,而不必了解其他视图控制器。
我的问题是,由于要构造视图控制器,我需要一个导航器,要构造导航器,我需要一个导航控制器,但是我想使视图控制器成为导航控制器的根,所以最好的方法是什么以尊重依赖注入最佳实践的方式解决这种循环依赖?
下面是由Sundell演示的Navigator模式的想法
protocol Navigator {
associatedtype Destination
func navigate(to destination: Destination)
}
class LoginNavigator: Navigator {
enum Destination {
case loginCompleted(user: User)
case signup
}
private weak var navigationController: UINavigationController?
private let viewControllerFactory: LoginViewControllerFactory
init(navigationController: UINavigationController,
viewControllerFactory: LoginViewControllerFactory) {
self.navigationController = navigationController
self.viewControllerFactory = viewControllerFactory
}
func navigate(to destination: Destination) {
let viewController = makeViewController(for: destination)
navigationController?.pushViewController(viewController, animated: true)
}
private func makeViewController(for destination: Destination) -> UIViewController {
switch destination {
case .loginCompleted(let user):
return viewControllerFactory.makeWelcomeViewController(forUser: user)
case .signup:
return viewControllerFactory.makeSignUpViewController()
}
}
}
class LoginViewController: UIViewController {
private let navigator: LoginNavigator
init(navigator: LoginNavigator) {
self.navigator = navigator
super.init(nibName: nil, bundle: nil)
}
private func handleLoginButtonTap() {
navigator.navigate(to: .loginCompleted(user: user))
}
private func handleSignUpButtonTap() {
navigator.navigate(to: .signup)
}
}
现在在AppDelegate
中,我想做
let factory = LoginViewControllerFactory()
let loginViewController = factory.makeLoginViewController()
let rootNavigationController = UINavigationController(rootViewController: loginViewController)
window?.rootViewController = rootNavigationController
但是我必须以某种方式将rootNavigationController
传递到factory
中,以便正确构建loginViewController
?因为它需要一个导航器,所以需要导航控制器。该怎么做?
答案 0 :(得分:2)
我最近还试图实现Sundell的Navigator模式,并遇到了同样的循环依赖。我必须在初始导航器中添加一些其他行为才能处理此奇怪的引导问题。我相信您的应用中的后续Navigator可以完美地遵循博客的建议。
这是使用JGuo(OP)示例的新的初始Navigator代码:
confirmPDF.blade.php
<html>
.
.
.
<div class="container">
@include('showPDF') //it's the file with the code that genarete the PDF
</div>
<div class="foot-buttons">
...
</div>
</body>
</html>
现在在AppDelegate中:
class LoginNavigator: Navigator {
enum Destination {
case loginCompleted(user: User)
case signup
}
private var navigationController: UINavigationController?
// This ^ doesn't need to be weak, as we will instantiate it here.
private let viewControllerFactory: LoginViewControllerFactory
// New:
private let appWindow: UIWindow?
private var isBootstrapped = false
// We will use this ^ to know whether or not to set the root VC
init(appWindow: UIWindow?, // Pass in your app's UIWindow from the AppDelegate
viewControllerFactory: LoginViewControllerFactory) {
self.appWindow = appWindow
self.viewControllerFactory = viewControllerFactory
}
func navigate(to destination: Destination) {
let viewController = makeViewController(for: destination)
// We'll either call bootstrap or push depending on
// if this is the first time we've launched the app, indicated by isBootstrapped
if self.isBootstrapped {
self.pushViewController(viewController)
} else {
bootstrap(rootViewController: viewController)
self.isBootstrapped = true
}
}
private func makeViewController(for destination: Destination) -> UIViewController {
switch destination {
case .loginCompleted(let user):
return viewControllerFactory.makeWelcomeViewController(forUser: user)
case .signup:
return viewControllerFactory.makeSignUpViewController()
}
}
// Add these two new helper functions below:
private func bootstrap(rootViewController: UIViewController) {
self.navigationController = UINavigationController(rootViewController: rootViewController)
self.appWindow?.rootViewController = self.navigationController
}
private func pushViewController(_ viewController: UIViewController) {
// Setup navigation look & feel appropriate to your app design...
navigationController?.setNavigationBarHidden(true, animated: false)
self.navigationController?.pushViewController(viewController, animated: true)
}
}
答案 1 :(得分:0)
这解决了吗?在AppDelegate中:
let factory = LoginViewControllerFactory()
let navController = UINavigationController()
let loginNavigator = LoginNavigator(navigationController: navController, viewControllerFactory: factory)
loginNavigator.navigate(to: .signup) // The example doesn't have a .login Destination, but it can easily be added to the factory, so using .signup instead
window?.rootViewController = navController
答案 2 :(得分:0)
我建议不要将rootViewController作为LoginViewControllerFactory的属性,而建议在调用“ make”函数时将其作为参数传递:
return viewControllerFactory.makeWelcomeViewController(forUser: user, with: rootViewController)