よくあるやり方
let name = "Main"
let identifier = "ArticleViewController"
let storyboard = UIStoryboard(name: name, bundle: nil)
let viewController = storyboard.instantiateViewControllerWithIdentifier(identifier) as! ArticleViewController
- このまま乱立すると、nameやidentifierの管理コストがひどい
-
as!
でキャストするのはswiftでコード書いてると気持ち悪い感が。。
こういうふうに書けるようにできます
let storyboard = UIStoryboard(storyboard: .Main)
let viewController: ArticleViewController = storyboard.instantiateViewController()
定義方法
Storyboardのidentifierをプロパティとしてもつprotocolを定義します。
protocol StoryboardIdentifiable {
static var storyboardIdentifier: String { get }
}
protocol extensionで初期値はUIViewControllerのクラス名がidentifierとなるように定義します。
そのため、Storyboard IDは定義しているViewControllerと同じ名前を使うようにしてください。(一番上の例だとStoryboard ID = "ArticleViewController"
となります)
extension StoryboardIdentifiable where Self: UIViewController {
static var storyboardIdentifier: String {
return String(self)
}
}
UIViewControllerにStoryboardIdentifiableを適合させます。
extension UIViewController: StoryboardIdentifiable { }
UIStoryboardを拡張します。
extension UIStoryboard {
// アプリ内で作成されているStoryboardの名前をenumで定義します
enum Storyboard: String {
case Main
case News
case Favorite
}
// 上で定義したenumでinitできるように
convenience init(storyboard: Storyboard, bundle: NSBundle? = nil) {
self.init(name: storyboard.rawValue, bundle: bundle)
}
// StoryboardIdenfiableプロトコルに適合してるUIViewControllerのみ
func instantiateViewController<T: UIViewController where T: StoryboardIdentifiable>() -> T {
// ジェネリクスでプロトコルに適合してるのは保証される
let optionalViewController = self.instantiateViewControllerWithIdentifier(T.storyboardIdentifier)
guard let viewController = optionalViewController as? T else {
fatalError("something error")
}
return viewController
}
}
UIViewControllerの継承はいつも通りで使えるようになります。
class ArticleViewController: UIViewController { }
参考リンク
UIStoryboard: Safer with Enums, Protocol Extensions and Generics — Swift Programming — Medium