こんにちは。
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にも接続できてしまう問題は依然として残っていますので
リリースまでに解決されることを期待しています。