Libra Studio Log

開発に関することやゲーム、ガジェットなどについてつらつらと書き記しています

SwiftでのStoryboard管理方法

f:id:daihase:20190809100049p:plain

こんばんわ、daihaseです。

新型iPhoneが発表になって連日TLのiOS開発者がXcode9GMをいじり倒したり色々シミュレーターでアプリの動作報告をしてくれてますね。僕は特に何の役にもたたないので、今日はStoryboardの管理の仕方なんかを書いて見ました。

 

iOSアプリでStoryboardを使った画面遷移など皆さんどうされてるでしょうか?

各ViewControllerに毎回Storyboardから該当のViewControllerを取得し、って書いてくとIdentifierのタイプミスなど起きたり、またStoryboard上でStoryboard IDを変えた時など、宣言した各場所全て書き換えたりと大変ですよね。

そこで僕なんかはStoryboardBuilder.swiftのようなものを用意し、そこに全て宣言し管理するようにしています。

import UIKit

class StoryboardBuilder: NSObject {
    var mainStoryboard: UIStoryboard!

    override init() {
        super.init()
        self.mainStoryboard = StoryboardBuilder.createRegisterStoryboard()        
    }
    
    static var sharedInstance: StoryboardBuilder = {
        return StoryboardBuilder()
    }()
    
    class func storyboardWithIdentifier(identifier: String) -> UIStoryboard {
        return UIStoryboard(name: identifier, bundle: Bundle.main)
    }

    // Main.Storyboard生成
    class func createRegisterStoryboard() -> UIStoryboard {
      return StoryboardBuilder.storyboardWithIdentifier(identifier: "Main")
    }
    
    // ログイン画面
    func loginViewController() -> LoginViewController {
        return mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
    }

    // チュートリアル画面
    func tutorialViewController() -> TutorialViewController {
        return mainStoryboard.instantiateViewController(withIdentifier: "TutorialViewController") as! TutorialViewController
    }
  
    // Email登録画面
    func emailRegistrationViewController() -> EmailRegistrationViewController {
        return mainStoryboard.instantiateViewController(withIdentifier: "EmailRegistrationViewController") as! EmailRegistrationViewController
    }
}

 

シングルトンになっていて、それぞれViewController上で以下のように取得するようなイメージです。

let loginViewController = StoryboardBuilder.sharedInstance.loginViewController()
let navigationController = UINavigationController(rootViewController: loginViewController)

UIApplication.shared.keyWindow?.rootViewController = navigationController

 

StoryboardBuilder.sharedInstance."呼び出したいコントローラー名" みたいに生成します。これならStoryboard上でStoryboard IDを後から変えた時とかも、StoryboardBuilder.swift内だけを修正するだけでいいですよね。

 

他にはもっとシンプルにするなら、.storyboardのファイル名とStoryboard IDを照らし合わせて取得する方法なんかも。

import Foundation
import UIKit

extension UIViewController {
    static func instantiateFromSelfStoryboard<T: UIViewController>(_: T.Type) -> T {
        return T.instantiateFromStoryboard(T.self, T.nameOfClass, T.nameOfClass)
    }
    
    static func instantiateFromStoryboard<T: UIViewController>(_: T.Type, _ storyboardName: String, _ identifier: String) -> T {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        return storyboard.instantiateViewController(withIdentifier: identifier) as! T
    }
}

UIViewControllerを拡張したクラスを作成。ここでコントローラー名が引数に指定されたら、それと同じ名前のStoryboardを取得し該当するViewControllerを返す、という感じです。

import Foundation

extension NSObject {
    static var nameOfClass: String {
        return NSStringFromClass(self).components(separatedBy: ".").last!
    }
    
    var nameOfClass: String {
        return NSStringFromClass(type(of: self)).components(separatedBy: ".").last!
    }
}

 

こちらは上のUIViewController+Extended.swiftから指定された、プロジェクト名.コントローラー名のコントローラー部分を切り出して文字列として返すクラスですね。

 

この2つを用意しておけば、各ViewControllerで以下のように書くだけで該当のStoryboardからViewControllerを取得出来ます。

let vc = UIViewController.instantiateFromSelfStoryboard(TutorialViewController.self)

 

これでTutorialViewController.storyboard上のTutorialViewController、そこからインスタンス生成出来るようになります。

今日はここまで。 それでは良いSwiftライフを〜