@IBDesignableをハックしてコードでの設定をRunせず確認する

InterfaceBuilderって便利ですよね。
僕は個人プロジェクトを作ることが多く、きっちりデザインが決まっているわけでもないため、UIを調整するのが楽で重宝しています。

AutoLayoutに加え、Xcode7からはStackViewも登場したことで、UIの作成はよりし易いものとなりました。


## コードで書くとすぐにUIを確認出来ない

IBは便利なのですが、どこを設定したのかというのが管理しにくくなってしまうという難点もあります。

なので極力コードで書きたいという気持ちがあるわけです。

そもそもAutoLayoutやStackViewを使いたいというのは **位置や大きさ** をグラフィカルに決めたいということで、
色やBorderStyle、PlaceholderはIBから設定する意味も薄いんですよね。

そこで、以下のように部品の設定をコードで書いたりするわけですが、
こうすると今度はRunをさせないと見た目を確認出来なくなってしまいます。

```swift:コードで設定その1
final class HomeView: UIView {
    @IBOutlet weak var nameTextField: NameTextField!

    override func layoutSubviews() {
        nameTextField.placeholder = "Name"
    }
}
```

```swift:コードで設定その2
class NameTextField: UITextField {
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        placeholder = "Name"
    }
```

**ということでコードで書いた設定をRunせずにIB上で確認出来るようにします。**


## 通常のIBDesignableとIBInspectableの使用方法

そこで使用するのが、IBDesignableとIBInspectableというものなのですが、
今回は通常とは少し違う方法で使用します。

先にこれら2つがどういうものかを説明しておきます。

修飾子 | 説明
:-- | :--
IBDesignable | UI部品でclass宣言の前に指定する修飾子。IBInspectableなパラメータに変化があったとき、その変更を反映出来るようにする。
IBInspectable | IBDesignableなclassでプロパティ宣言の前に指定する修飾子。IB上で変更可能なパラメータを追加する。


本来これらは、IB上で指定できない設定をIB上で設定出来るようにするために使うものだと思っています。

以下はBorderWidthをIB上で指定できるようにしたコードです。

```swift
@IBDesignable final
class NameTextField: UITextField {
    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
```

このように動きます。

![borderwidth.gif](https://qiita-image-store.s3.amazonaws.com/0/66391/2acb00e7-fb71-3119-16cd-f58662c211f4.gif "borderwidth.gif")


そしてこれらをちょっと違った使い方で使用してみます。


## コードでの設定を即時反映させる

先ほどの例を見て分かる通り、IBInspectableなパラメータの値を変更すると、そのプロパティのdidSetに書いたコードが動く、ということが起こります。

ですが先述した通り、IB上で意味のある設定をしたくないので、
ここでは **コードでの設定を反映させる** というプロパティを設定します。


今回は例として以下のようなTextFieldをコードで設定し、IB上に表示させます。

![HomeView_xib.png](https://qiita-image-store.s3.amazonaws.com/0/66391/85654f85-1dd0-2e38-3e13-5f34e4a0ec6e.png "HomeView_xib.png")


まず、コードは以下のように設定します。

仕組みとしては、triggerというプロパティに変化があったとき、setupメソッドが動き、そこで全ての設定をする、という感じになっています。

```swift:NameTextField.swift
@IBDesignable final
class NameTextField: UITextField {
    @IBInspectable var trigger: Bool = true {
        didSet {
            setup()
        }
    }
    
    private func setup() {
        borderStyle = .None
        textAlignment = .Center
        textColor = MyColor.gray()
        font = font?.fontWithSize(18)
        placeholder = "Name"
    }
}
```

```swift:TextFieldContainer.swift
@IBDesignable final
class TextFieldContainer: UIView {
    @IBInspectable var trigger: Bool = true {
        didSet {
            setup()
        }
    }
    
    private func setup() {
        let borderBottom = CALayer()
        let borderWidth: CGFloat = 1
        
        borderBottom.removeFromSuperlayer()
        borderBottom.frame = CGRectMake(0, frame.height - borderWidth, frame.width, borderWidth)
        borderBottom.backgroundColor = MyColor.red().CGColor
        layer.addSublayer(borderBottom)
    }
}
```

これをIB上の部品にclass適用させ、triggerをONにします。
すると、以下のようにコードの設定が反映されることが分かります。

![trigger.gif](https://qiita-image-store.s3.amazonaws.com/0/66391/3f9e5757-3599-d5cf-ca28-e153fe60e677.gif "trigger.gif")


これで管理がしやすいコードでの設定を即時確認をしつつ、位置とサイズのみをIB上から指定出来るようになりました。


## 最後に
IBDesignableとIBInspectableはコードを書くときにXcodeがクラッシュすることが多く、少し使いにくいです。
みなさんも実際に試してみる時はXcodeの機嫌を取りつつ使ってみてください。