@IBInspectableでプロパティに列挙型を使いたい
@IBDesignableと@IBInspectableとは
アプリを作る際にUIView
やNSView
を拡張してカスタムViewを作るのは良くあることだ。作成したカスタムViewの使い方は簡単で、XcodeのStoryboardで使用する場合はViewを配置してインスペクタでClassを作成したカスタムViewにするだけで良い。
が、カスタムViewを作る際にそのままUIView
のSubclassとして作ってしまうとStoryboard上でUIが何も表示されずに真っ白になってしまう。
これをカスタムViewの内容も表示されるようにするのが@IBDesignable
で、インスペクタでプロパティの値の設定をできるようにするのが@IBInspectable
だ。
@IBDesignableでカスタムViewの内容を表示する
@IBDesignable
を使用しない場合のカスタムViewはStoryboardで下記のように表示される。
真っ白な状態で選択するとアンカーが表示されるだけで、正直見やすいとは言い難い。
が、@IBDesignable
を使えば以下のようにカスタムViewの内容も表示されるようになる。(メーターのメモリが表示されているだけなのでサンプルとしては不適当だが…)
この@IBDesignable
は以下のように作成したカスタムViewのclassの前に挿入して使用する。
@IBDesignable class GPLevelMeterView: UIView
これだけでXcodeのStoryboardでカスタムViewの内容も表示されるようになるのだ。
@IBInspectableでインスペクタで値を設定できるようにする
StoryboardではUIパーツを選択するとプロパティの値をインスペクタで設定できる。カスタムViewでも親クラスのプロパティの値はインスペクタに表示されるので設定することができるが、カスタムView独自のプロパティはインスペクタに表示されない。
これをインスペクタに表示して設定できるようにするのが@IBInspectable
だ。
カスタムViewのプロパティ宣言に@IBInspectable
を挿入すればそのプロパティがインスペクタに表示される。
@IBInspectable var zeroCenter: Bool = false
そしてこのプロパティzeroCenter
は下記のようにインスペクタに表示される。
ただし、気を付けなくてはいけないのは@Inspectable
で扱えるプロパティの型。Appleのドキュメントによると
You can add the IBInspectable attribute to any property in a class declaration, class extension, or category of type: boolean, integer or floating point number, string, localized string, rectangle, point, size, color, range, and nil.
となっている。
実際に使えるのは以下の通り。
- Bool
- Int
- Double
- Float
- String
- CGRect
- CGPoint
- CGSize
- UIColor
- UIImage
実はドキュメントにないUIImage
もインスペクタに表示される。
またドキュメントにあるrangeはNSRange
を@IBInspectable
にするとインスペクタにはフォームが表示されるがDesignables Build failedとなってしまいカスタムビューが表示されない。
コードの方でもProperty cannot be marked @IBInspectable because its type cannot be represented in Objective-Cとエラーが表示される。
列挙型(enum)を@IBInspectableにしたい場合
@IBInspectable
を使う場合に問題になるのがプロパティが列挙型(enum)で宣言された型だった場合。例えば以下のコードはエラーになってしまう。
enum ScalePosition: Int {
case none = 0, left, top, right, bottom
}
@IBInspectable var scalePosition: ScalePosition = .none
@IBInspectable
はScalePosition
型には対応してないのが原因だ。
これを回避するには@IBInspectable
用に別のプロパティを加える方法がある。
enum ScalePosition: Int {
case none = 0, left, top, right, bottom
}
var scalePosition: ScalePosition = .none
@available(*, unavailable, message: "This property is reserved for IB. Use 'scalePosition' instead.", renamed: "scalePosition")
@IBInspectable var scalePositionValue: Int {
get {
return self.scalePosition.rawValue
}
set(newValue) {
self.scalePosition = ScalePosition(rawValue: newValue) ?? .none
}
}
scalePositionValue
は列挙型で宣言されているscalePosition
を@IBInspectable
が扱えるInt
に変換しているだけ。
これでインスペクタにはscalePositionValue
が表示されて値を設定できるようになる。
ちなみに@available
を使うことでコードからscalePositionValue
にアクセスしようとした場合にscalePosition
を使うようメッセージを出している。renamed
でscalePosition
を指定すればFix
ボタンで修正をすることもできる。
まとめ
StoryboardでカスタムViewの内容を確認しながら配置できるようになる@IBDesignable
とそのプロパティをインスペクタで設定できるようにする@IBInspectable
は、自分でカスタムViewを作ったことがある人なら一度は書いたことのあるコードだろう。
今回は列挙型(enum)を@IBInspectable
にしてインスペクタでポップアップメニューを使って選択できる形にしたくて調べてみたのだが、結局ポップアップメニューでの選択の方法は見つからなかった。
列挙型を使うためにcomputedプロパティを使う方法は下記で見つけた。
今回作った@IBDesignable
と@IBInspectable
を使ったカスタムビューのサンプルはGitHubに置いてある。
Discussion