iOS Swift 带有引导屏幕的应用程序的正确导航结构是什么?

What's the right navigation structure for iOS Swift app with onboarding screens?

我正在创建一个按如下方式运行的应用程序

"Getting Started" 屏幕非常独立,因此我为它们创建了一个故事板,而主应用程序将以编程方式创建。

(2) 的逻辑应该放在哪里?我正在考虑创建某种 UINavigationController,它位于两组屏幕之上,可以决定显示什么。或者,我打算只启动主应用程序 VC 并让逻辑将 "Getting Started" 显示为模态在那里。

请问有什么最佳实践建议吗?

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

然后使用UserDefault来存储用户是否点击"get started"

        // 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()))
    }

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

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

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

尝试将您的代码保持在适当的 class 或结构中:决定显示哪个屏幕以及如何创建视图控制器的逻辑必须在适当的 class 或结构中。此逻辑既不属于 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 被关闭时,您可以调用工厂的 generateHomeScreen() 函数来获取主屏幕并切换到主屏幕。