こんにちは。
GANMA! チームで開発をしている、寺坂です。
GANMA!のiOSアプリでは、
Xcodeの InterfaceBuilder を活用してUI開発をしています。
今回は、InterfaceBuilderをより活用できようになる、かもしれない内容を書いていきます。
モチベーション
UITableViewのdalegateやdataSourceのように
自分で作ったViewのdelegateもInterfaceBulder(以下、IB)上で Outlet接続したい。
前提
- Xcode8系
既知の問題
@IBOutletをつけても、delegateのようなprotocolはOutletで接続できない。

問題を回避する方法
型情報を一度AnyObjectにしてしまい、IBを騙す。
- CustomViewクラスを作る
@objc付きのprotocolでdelegateを定義- 定義したdelegateをどこかで実装する
@IBOutlet付きvar delegateをAnyObject型で定義する- IBでdelegateが接続できるようになっているので好きな場所へ接続する
var delegateの型をCustomViewDelegateにする
具体的な手順説明
1. CustomViewクラスを作る
自作Viewを用意します。
これをIBに配置します。
class CustomView: UIView { ... }
2. @objc付きのprotocolでdelegateを定義
@objcをつけないと、@IBOutletをつけられません。
一方@objcをつけたので、いつもの:class は不要です。
@objc protocol CustomViewDelegate { }
3. 定義したdelegateをどこかで実装する
delegateを繋げたい対象を用意します。
ViewControllerを肥大化させたくない場合は、NSObject継承のクラスを用意しても構いません。
class CustomObject: NSObject { ... }
extension CustomObject: CustomViewDelegate { ... }
これをStoryboardなどに配置しておく。
4. @IBOutlet付きvar delegateをAnyObject型で定義する
CustomViewDelegate型をそのまま指定しても、IB上ではどのObjectも反応しません。
AnyObjectにすると、IB上にあるObjectが反応するようになります。
@objc protocol CustomViewDelegate { }
class CustomView: UIView {
@IBOutlet private weak var delegate: AnyObject?
...
}
[注意点]
AnyObjectにすると、どのObjectにも反応するので、
間違って別のObjectに対して接続してしまわぬよう注意が必要です。
CustomViewDelegateを実装しているObjectだけに
接続できるようにする方法は今の所なさそうです。
5. IBでdelegateが接続できるようになっているので好きな場所へ接続する
楽しい時間ですね。

6. var delegateの型をCustomViewDelegateにする
一度接続してしまえば、AnyObjectをCustomViewDelegateに変えても維持されます。
@objc protocol CustomViewDelegate { }
class CustomView: UIView {
@IBOutlet private weak var delegate: CustomViewDelegate?
...
}
得られたものと失ったもの
[得られたもの]
- わざわざコードで
delegate = selfとかしなくてもOutlet接続で済ませられるようになった - delegateを
privateにできるようになった - NSObjectを継承したクラスを用意してdelegateを分離したい時、このクラスのインスタンスを保持しておかなくてもStoryboard上に配置して接続するだけでよくなった
[失ったもの]
- 型安全。CustomViewDelegateを実装していないObjectに接続してしまうと実行時に死ぬ
Xcode9ではどうなるのか
Xcode9 Beta3時点から、一時的にAnyObject型にする手順が不要となっています。
ただし、どのObjectにも接続できてしまう問題は依然として残っていますので
リリースまでに解決されることを期待しています。