【问题标题】:What's the right navigation structure for iOS Swift app with onboarding screens?带有引导屏幕的 iOS Swift 应用程序的正确导航结构是什么?
【发布时间】:2020-05-31 09:33:30
【问题描述】:

我正在创建一个如下工作的应用程序

  • 启动屏幕
    • 一个。如果已查看并接受“入门”屏幕,则打开主应用
    • 乙。如果未查看/接受,则显示“开始使用”,然后在接受后显示主应用

“入门”屏幕非常独立,因此我为它们创建了故事板,而主应用程序将以编程方式创建。

(2) 的逻辑应该放在哪里?我正在考虑创建某种 UINavigationController,它可以位于两组屏幕之上,并且可以决定要显示的内容。或者,我打算只启动主应用程序 VC,然后让逻辑显示“入门”作为模式。

请对最佳实践有任何建议吗?

【问题讨论】:

    标签: ios swift storyboard


    【解决方案1】:

    没有确定的答案。但是,您可以尝试稍微预测一下您的需求:

    • 如果入门屏幕是您唯一会以这种方式显示的内容,那么您可以在 AppDelegate 或主视图控制器中添加一些代码,
    • 如果您计划在某些版本之后显示这种屏幕(例如展示新功能),您可以使用某种模式机制来显示可以关闭的全屏模式。

    无论您选择哪种解决方案:

    尝试将您的代码保存在适当的类或结构中:决定要显示什么屏幕以及如何创建视图控制器的逻辑必须在适当的类或结构中。这个逻辑既不属于AppDelegate,也不属于你的主视图控制器——顺便说一句,这就是你以巨大而凌乱的视图控制器结束的方式。

    根据您的需要(在多个同类实体之间进行选择),您可以查看factory design pattern。它是一种封装逻辑以创建实体的通用结构。您可以将其设计为在您的控制器之间进行选择:

    class RootViewControllerFactory {
    
      var rootViewController: UIViewController {
        if shouldDisplayOnboardingScreen() {
          return generateOnboardingScreen()
        } else {
          return generateHomeScreen()
        }
      }
    
      private func shouldDisplayOnboardingScreen() -> Bool {
        // Your logic to decide whether you should display it or not.
      }
    
      func generateOnboardingScreen() -> UIViewController {
        // Load it from your storyboard
      }
    
      func generateHomeScreen() -> UIViewController {
        // Load it programmatically
      }
    
    }
    

    然后,您的AppDelegate 可以使用正确的视图控制器,但代码不会在加载代码中混淆。这使阅读更容易:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        self.window = UIWindow(frame: UIScreen.main.bounds)
    
        self.window?.rootViewController = RootViewControllerFactory().rootViewController
        self.window?.makeKeyAndVisible()
    
        return true
    }
    

    通过这个方案,当onboarding view controller被dismiss时,你可以调用你工厂的generateHomeScreen()函数来获取主屏幕并切换到它。

    【讨论】:

    • 深入研究了这一点,发现这篇文章建议不要更换根 VC,而是使用容器根 VC。我想我也可以将要显示的内容的逻辑放在 Root VC 中,或者让工厂对其进行抽象:How to replace the rootViewController of the UIWindow in iOS
    • 你是对的,这也是一个解决方案:拥有一个包含 VC 来选择要显示的内容。我看到的唯一丑陋的部分是你将永远拥有这个包含 VC 的东西,即使你知道你再也不需要它了。如果你想避免切换你的rootViewController,你也可以移动你的homeViewController中的逻辑并在主视图顶部加载入职VC(全屏模式解决方案)。
    【解决方案2】:

    在 AppDelegate.h/SceneDelegate.h 中使用代码启动 仅供参考,我相信您仍然可以混合使用情节提要 ViewController 和代码 ViewController。但是我建议只坚持一种风格(在这种情况下,以编程方式编写 ViewController)

    然后使用 UserDefault 存储用户是否点击“开始”

            // Set Root VC
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.windowScene = windowScene
    
        let navigator = Container.shared.resolve(INavigator.self)!
        let localStorageService = Container.shared.resolve(ILocalStorageService.self)!
        if localStorageService.isKeyExist(key: Configs.KEY_USER) {
            navigator.switchRoot(keyWindow: window!, scene: .main(viewModel: MainTabBarViewModel()))
        } else {
            navigator.switchRoot(keyWindow: window!, scene: .intro(viewModel: IntroViewModel()))
        }
    

    【讨论】:

    • 谢谢!顺便说一句,你为什么不混合故事板 VC 和代码 VC?只是复杂性还是其他原因?
    • 另外,您将如何处理被解雇的介绍性 VC 以启动主应用程序?在介绍 VC 或其他地方委托的按钮点击操作?
    • 1.为什么不混?有没有好坏之分?坚持一种风格,使其易于维护。在我的情况下,我在这个应用程序中使用 MVVM + RxSwift 架构,我使用 Xib for View.2。如何关闭 introVC?无需解散,只需在此处替换您的 keywindow 中的 root viewController。navigator.switchRoot 是我用来解耦导航逻辑的 Navigator 方法。在下面我简单地替换 Keywindow 的 root viewcontroller
    猜你喜欢
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 1970-01-01
    • 2019-03-17
    • 2017-08-10
    相关资源
    最近更新 更多