LoginSignup
44

More than 5 years have passed since last update.

UIViewControllerに、同名のStoryboardから生成する能力を与える

Last updated at Posted at 2015-10-31

iOSの開発をしていると、UIViewControllerと同名のStoryboardを用意しておいて、UIViewControllerを生成するコードがよく書くと思います。

Storyboardから生成
let storyboard = UIStoryboard(name: "SampleViewController", bundle: nil)
let viewController = storyboard.instantiateInitialViewController() as! SampleViewController

覚えてしまえば簡単なコードですが 出来れば共通化したいところです。

共通化

そこで Swift 2.0 から追加された「protocol extension」を使って、UIViewControllerにその能力を与えられるようにしてみました。

具体的には、以下のようにプロトコルを採用するだけでその能力が得られるようにしてみました。

// ViewControllerの定義
class SampleViewController: UIViewController, StoryboardInitializable {
}

// 生成
let viewController = SampleViewController.instantiateStoryboard()

仕組み

以下のようにプロトコルとその拡張を用意することで実現しています。

実装
protocol StoryboardInitializable {
}

extension StoryboardInitializable where Self: UIViewController {

    static func instantiateStoryboard() -> Self {
        let type = Mirror(reflecting: self).subjectType
        let name = String(type).componentsSeparatedByString(".")[0] // クラス名を取得
        let storyboard = UIStoryboard(name: name, bundle: nil)
        let viewController = storyboard.instantiateInitialViewController() as! Self
        return viewController
    }
}

Xibからの生成にも対応する

UIViewを同名のNibファイル(.xib)から生成することもよくあります。こちらも同じ仕組みで共通化してみました。

使い方
// Viewの定義
class SampleView: UIView, XibInitializable {
}

// 生成
let view = SampleView.instantiateXib()
実装
protocol XibInitializable {
}

extension XibInitializable where Self: UIView {

    static func instantiateXib() -> Self {
        let type = Mirror(reflecting: self).subjectType
        let name = String(type).componentsSeparatedByString(".")[0] // クラス名を取得
        let nib = UINib(nibName: name, bundle: nil)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! Self
        return view
    }
}

全てのUIViewController/UIViewに適用する

プロトコルの採用すら省略したい、という場合には以下のようにUIViewController/UIViewをextensionで拡張することで何も書かずともStoryboard/xibから生成する能力を得られます。

UIViewController/UIViewを拡張して全てに適用する
extension UIViewController : StoryboardInitializable {
}

extension UIView : XibInitializable {
}

// 何も書かかなくても使えるようになる
let viewController = SampleViewController.instantiateStoryboard()
let view = SampleView.instantiateXib()

ただしこうした場合、同名のStoryboard/xibから生成することを意図していないクラスに対しても呼べてしまうので注意が必要です。(が、実際に不都合が起こるケースは稀かと思います)

まとめ

  • UIViewControllerでStoryboardInitializableプロトコルを採用すると、同名のStoryboardから生成する能力を得られます
  • UIViewでXibInitializableプロトコルを採用すると、同名のNibファイル(.xib)から生成する能力を得られます
  • Swift 2.0 から導入された「protocol extensions」は便利!

コード全体:
https://github.com/YusukeHosonuma/SwiftCommons/blob/master/SwiftCommons/ResourcesInitializable.swift

SwiftCommons

Swiftの勉強用として作り始めた共通ライブラリです。発展途上ですが、MITライセンスですのでよろしければご利用ください。
https://github.com/YusukeHosonuma/SwiftCommons

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44