RootViewControllerで画面遷移をまとめた話

 RootViewControllerで画面遷移をまとめた話

煩雑になりがちな画面遷移をRootViewControllerクラスでまとめた話。

サンプルコード
https://github.com/SatoTakeshiX/RootViewController

参考サイト
iOSアプリを作るときのおすすめ構成 – Swift・iOSコラム – Medium
https://medium.com/swift-column/ios-2017-4f04d00a5804

iOS: Root Controller Navigation – Stan Ostrovskiy – Medium
https://medium.com/@stasost/ios-root-controller-navigation-3625eedbbff

AppRootControllerのご提案(簡略説明版)
https://speakerdeck.com/yimajo/approotcontrollerfalsegoti-an-jian-lue-shuo-ming-ban

iOS 9から追加された3D Touch shortcutsを実装してみる
https://qiita.com/koogawa/items/dd94a303c7852885f269

53e2d354b3299d64a54af680865516d5?s=128

Sato Takeshi

April 03, 2018
Tweet

Transcript

  1. RootViewControllerͰ ը໘ભҠΛ·ͱΊͨ࿩ ROPPONGI.swift ୈ2ճ 2018೥4݄2೔ ࠤ౻߶࢜ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 1

  2. Who am I ࠤ౻߶࢜ (@hatakenokakashi) MAMORIOגࣜձࣾͰiOSΤϯδχΞ͠ ͍ͯ·͢ɻ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 2

  3. UIViewControllerͷભҠํ๏͓͞Β ͍ present(nextVC, animated: true, completion: nil) navigationController?.pushViewController(nextVC, animated: true)

    ·ͨ͸segueΛ࢖͏ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 3
  4. ը໘ભҠͷͳ΍ΈͲ͜Ζ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 4

  5. ը໘ભҠ͸ભҠݩͷը໘͔Β࣮ߦ ↓ ։ൃ͕͢͢ΜͰɺը໘͕૿͑ͯ͘Δͱͭ Β͍ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 5

  6. ͭΒ͍͜ͱ • ΞϓϦશମͷը໘ભҠ͕Θ͔Γʹ͘͘ͳΔ(ͦΕͧΕͷ ViewControllerΫϥεͷ࣮૷Λ1ͭͮͭΈͳ͍ͱ͍͚ͳ͍) • ༷ʑͳ৚݅Ͱىಈը໘Λมߋ͍ͨ͠ͱ͖ίʔυ͕൥ࡶʹͳΔ • ະϩάΠϯ/ϩάΠϯͰىಈ࣌ͷViewControllerΛ෼͚͍ͨͱ͖ • ϓογϡ௨஌ɺ3DTouchγϣʔτΧοτͳͲͰಛఆͷը໘Λ

    දࣔ͢Δͱ͍͏࣮૷͕ਏ͍ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 6
  7. RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 7

  8. RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 8

  9. func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler

    completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { let naviCon = application.windows[0] .rootViewController as? UINavigationController RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 9
  10. //࠷ޙʹද͍ࣔͯͨ͠ը໘Λ൑அͯ͠ભҠΛ͢Δ //ද͍ࣔͯ͠ΔViewController͕ϝΠϯϏϡʔͳΒઃఆը໘ʹભҠɻ //͢Ͱʹઃఆը໘Λද͍ࣔͯ͠ΔͳΒɺίϯςφϏϡʔΛදࣔͤ͞Δɻ if let visibleMainViewCon = naviCon?.visibleViewController as? CallMainViewController

    { visibleMainViewCon.performSegue(withIdentifier: "gotoSettingFromPush", sender: true) } else if let visibleSettingViewCon = naviCon?.visibleViewController as? SettingViewController { // visibleSettingViewCon. visibleSettingViewCon.isFromPush.value = true visibleSettingViewCon.hiddenReplyViews(ishidden: !visibleSettingViewCon.isFromPush.value) } else if !(naviCon?.viewControllers.last is SettingViewController) { let popVC = naviCon?.popViewController(animated: true) print("ϙοϓͨ͠VC͸ \(type(of: popVC))") if let visibleSettingViewCon = naviCon?.visibleViewController as? SettingViewController { visibleSettingViewCon.isFromPush.value = true visibleSettingViewCon.hiddenReplyViews(ishidden: !visibleSettingViewCon.isFromPush.value) } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 10
  11. ! RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 11

  12. RootViewControllerͰ ղܾ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 12

  13. RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 13

  14. ํ਑ • RootViewControllerͱ͍͏ΫϥεΛ࡞Δ • windowsͷrootViewControllerʹಥͬࠐΉ • ը໘ભҠ͸RootViewControllerʹࢠViewControllerΛ૊Έࠐ Ή͜ͱͰ࣮ߦ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 14

  15. αϯϓϧΞϓϦ঺հ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 15

  16. RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 16

  17. RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 17

  18. ϓϩδΣΫτͷMain InterfaceΛۭʹ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 18

  19. AppDelegate func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) ->

    Bool { setup() return true } private func setup() { window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = router.rootViewController window?.makeKeyAndVisible() } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 19
  20. private let _router = Router() extension AppDelegate { static var

    shared: AppDelegate { guard let delegate = UIApplication.shared.delegate as? AppDelegate else { fatalError("not set app delegate") } return delegate } var router: Router { return _router } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 20
  21. final class Router { enum Locate { case login case

    main case camera case setting } let rootViewController = RootViewController() func route(to locate: Locate, from viewController: UIViewController) { switch locate { case .login: rootViewController.showLoginScreen() break case .main: rootViewController.showMain() case .camera: rootViewController.showCameraScreen() break case .setting: rootViewController.showSettingScreen() break } } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 21
  22. class RootViewController: UIViewController { private var current: UIViewController init() {

    //ىಈ࣌͸ඞͣεϓϥογϡը໘Λදࣔ current = SplashViewController() super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.blue addChildViewController(current) current.view.frame = view.bounds view.addSubview(current.view) current.didMove(toParentViewController: self) } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 22
  23. // RootViewController // MARK: ΞϓϦͰߦ͏۩ମతͳભҠ func showLoginScreen() { let new

    = LoginViewController.instantiate() addChildViewController(new) new.view.frame = view.bounds view.addSubview(new.view) new.didMove(toParentViewController: self) replaceCurrent(for: new) } func showMain() { switch current { case let tab as TabViewController: tab.selectMain() default: let tab = TabViewController.instantiate() animateFadeTransition(to: tab) } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 23
  24. ࢖༻ྫ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 24

  25. Splashը໘ͷग़͠෼͚ func onViewDidload() { if UserDefaults.standard.bool(forKey: "IS_LOGIN") { //ϝΠϯը໘Λදࣔ AppDelegate.shared.router.route(to:

    .main, from: self) } else { UserDefaults.standard.set(true, forKey: "IS_LOGIN") //ϩάΠϯը໘Λදࣔ AppDelegate.shared.router.route(to: .login, from: self) } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 25
  26. 3D TouchγϣʔτΧοτͰಛఆը໘ ભҠ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 26

  27. struct DeepLinkShortcut { enum ItemType: String { case main case

    camera case setting } static func register() -> [UIApplicationShortcutItem] { let mainShortcutItem = UIApplicationShortcutItem(type: ItemType.main.rawValue, localizedTitle: "ϝΠϯը໘") let cameraShortcutItem = UIApplicationShortcutItem(type: ItemType.camera.rawValue, localizedTitle: "Χϝϥը໘") let settingShortcutItem = UIApplicationShortcutItem(type: ItemType.setting.rawValue, localizedTitle: "ઃఆը໘") return [mainShortcutItem, cameraShortcutItem, settingShortcutItem] } static func handleShortcut(_ shortcut: UIApplicationShortcutItem) -> ItemType? { return ItemType(rawValue: shortcut.type) } } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 27
  28. // AppDelegate private func setup() { window = UIWindow(frame: UIScreen.main.bounds)

    window?.rootViewController = router.rootViewController window?.makeKeyAndVisible() UIApplication.shared.shortcutItems = DeepLinkShortcut.register() } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 28
  29. // AppDelegate // MARK: Shortcuts func application(_ application: UIApplication, performActionFor

    shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { if let shortCut = DeepLinkShortcut.handleShortcut(shortcutItem) { let route = AppDelegate.shared.router switch shortCut { case .main: route.route(to: .main, from: route.rootViewController) case .camera: route.route(to: .camera, from: route.rootViewController) case .setting: route.route(to: .setting, from: route.rootViewController) } } completionHandler(true) } RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 29
  30. Demo RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 30

  31. ιʔείʔυ https://github.com/SatoTakeshiX/RootViewController RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 31

  32. ੵۃతʹϝϯόʔืूதʂ https://www.wantedly.com/companies/mamorio • iOSΤϯδχΞ • AndroidΤϯδχΞ • UI/UXσβΠφʔ RootViewControllerͰը໘ભҠΛ·ͱΊͨ࿩ 32