Storyboard or Xib? Storyboard and Xib.

> 日本語を勉強しています。上手く使えない場合が多いと思いますが、共有してみたいから、頑張って書きます。

この前、新規アプリを開発し始めた時、UI はコードか、Storyboard か、Xib か、どっちかで進めていくかなを考えました。

もちろん、コードで UI を実装できます、しかも数年前、コードで全ての画面を作る人が少なくない、でも、スクリーンサイズの充実化、開発ツールの進展に伴う、*Auto Layout*、*Size Classes* は誕生しました、コードで UI を実装するのは日々複雑になりました。

自分は Storyboard または Xib を使用して画面を作るのが好きだけど、やはりたまに迷う、 Storyboard と Xib どっちを選んだ方がいいですか？
	
## Storyboard or Xib
	
チームの旧バージョン iOS Guidelines にあるルールは：
	
> StoryBoard はコンフリクトしやすく、チーム開発には向いていないため、なるべく利用しない。各画面や画面部品ごとに .xib ファイルを用いる。

半分賛成半分反対。

一つの Storyboard に複数の ViewController がある場合、コンフリクトはぜったい発生しやすい、もしこの罠にはまると、めんどいことになるかもしれない、コンフリクトを解消するため、.xml を修正した経験がある人もいると思います。けれども、これは Storyboard の問題ではない、これをきっかけに Storyboard を諦めると、損になってしまうと思います。そもそも、Storyboard は Xib を置き換えるためのものではありません。

Storyboard が重視する点は画面の関係性と遷移関係、 Xib を利用して、共通コンポーネントを作って、再利用 (reuse) できるのはすごく良いだと思います。

この新規アプリはシリーズアプリなので、共通のデザインで実装していきました。アプリ内はいくつかの dialog があって、ほぼ header と  footer が付いています。こんな利用シーンで、Xib を利用していいんじゃないですかって思いましたので、結局 dialog を ViewController にした、Storyboard で Present Modally を利用して、遷移関係ははっきり見えます。header と  footer の部分は Xib を作って、独立な View として、必要に応じて、画面にロードできます。その上、`@IBInspectable` と `@IBDesignable` を定義すれば、結構使いやすくなります。メリットは画面デザインと遷移関係両方はっきり見えて、再利用も簡単にできます；デーメリットは *Auto Layout* の設定を何度もやらないといけませんかな。
	
## 心得

### Storyboard Reference
	
「コンフリクトは発生しやすい」ーー **Storyboard Reference** が誕生した後、こういう話は完全に誤解 (false statement) になりました。

**Storyboard Reference** は Xcode 7 からサポートされ始めました、部品ライブラリから画面へドラッグできます。既存の Storyboard を分解したいならば、*Editor -> Refactor to Storyboard* で実現できます。例えば、Container View は二個あって、こんな場合画面の中にある controller の数は三つです。分解して、下のようななります、Container View は別 Storyboard に分かれています：
	
![storyboard_reference.png](http://upload-images.jianshu.io/upload_images/26601-bd3a6ca5e58f8106.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

それで、チームワークでも、コンフリクトはほぼなくなるかな。もちろん、設計の良い構造を目指して工夫することはいちばん大切なことです。

### Loadable Nib

重複コードを減らせるため、型安全のため、Nib のロードをプロトコルにしよう！
	
```swift
protocol Loadable: class {
    static var nibName: String { get }
}

extension Loadable {
    static var nibName: String { return String(describing: Self.self) }
}
```
	
`UIView` の *Extension* を定義して、ロードされたい View は `Loadable` プロトコルを採用することが要求されます。ちなみに、Nib は Xib がコンパイルされた後のものです。
	
```swift
extension UIView {
    // Custom Class
    func instantiateFromNib<T: UIView>(_:T.Type) -> T where T: Loadable {
        if let nib = UINib(nibName: T.nibName, bundle: nil).instantiate(withOwner: nil, options: nil).first as? T {
            return nib
        } else {
            fatalError("Nib \(T.nibName) is not exist ?!")
        }
    }
    // File's Owner
    func instantiateFromNibOwner<T: UIView>(_:T.Type) where T: Loadable {
        let bundle = Bundle(for: type(of: self))
        if let nib = UINib(nibName: T.nibName, bundle: bundle).instantiate(withOwner: self, options: nil).first as? UIView {
            nib.frame = self.bounds
            nib.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            self.addSubview(nib)
        } else {
            fatalError("Nib \(T.nibName) is not exist ?!")
        }
    }
}
```
	
それで、簡潔なコードで初期化できます：
	
```swift
	let view:ClassName = self.instantiateFromNib(ClassName.self)
	self.instantiateFromNibOwner(ClassName.self)
```

> 追記：LoadableNib Library と Demo を [Github](https://github.com/Ckitakishi/LoadableNib) にアップされました。

ちょっと前、[Reusable](https://github.com/AliSoftware/Reusable) というライブラリを知りました。Nib の再利用について同じことをやっています、しかも、Cell、Storyboard、ViewController の再利用も実現されました、もっと強いと言えます。
	
Nib の再利用について、上記方法と *Reusable* の相違点は：
	
1. *Reusable* に、Nib を初期化する部分は `UIView` の *Extension* じゃなくて、*Protocol Extension* です。
2. *File's Owner* として再利用する時，*Reusable* は *Auto Layout* を使っていて、自分は frame から着手しました。。
	

### `@IBDesignable` 和 `@IBInspectable`
	
`@IBDesignable`：リアルタイムにビューを描画する。
`@IBInspectable`：*Runtime Attributes* を定義する。
	
例：`DialogHeaderView` を定義、前に `@IBDesignable` を付して、このクラスの `headerTitle` 属性を `@IBInspectable` 付きで定義します：
	
```swift
@IBDesignable class DialogHeaderView: UIView {
    
    @IBInspectable var headerTitle: String = "" {
        didSet {
            navigationBar.topItem?.title = self.headerTitle
        }
    }
    ...
}
```
	
ターゲットビューに `UIView` をおいて、*Custome Class* を `DialogHeaderView` に設定，この時 *Attribuite Inspector* に `@IBInspectable` 付きの属性に値を設置できます：
	
	
![IB1.png](http://upload-images.jianshu.io/upload_images/26601-eac9f255ccae1ca5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

	
*Runtime Attributes* 欄からも設置できますが：
	

![IB2.png](http://upload-images.jianshu.io/upload_images/26601-b62cde3e370e21a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

一瞬、ターゲットビューに `DialogHeaderView` がリアルタイムで表示されます〜
	
ちょっと残念ですが、リアルタイムビルドの途中に失敗することは少なくないです。念のため、デバッグ方法を覚えておいたほうがいい：*Editor -> Debug Selected Views*。だいたいすぐエラーを解消できると思います。

### 型安全
	
`Loadable` を除いて、型安全のため、ほかのできることまだあります。
	
Storyboard は存在するから、`Segue` の定義はぜったいあリます。`identifier` は文字列なので、ビューの文字列とコード中の文字列が合っていない場合はたまたまあることで、[R.swift](https://github.com/mac-cain13/R.swift) の導入をお勧めしたいです。
	
*R.swift* は広く使われて型安全の対応をしています、画像 (Image)、フォント (Font)、多言語 (Localization) なども、*R.swift* から得るところは少なくないです。