Interface Builder から独自プロパティを設定する : Objective-C プログラミング

PROGRAM


Interface Builder から独自プロパティを設定する

Xcode 4 では、User Defined Runtime Attributes という機能を使って、独自に実装したクラスのプロパティを Interface Builder を使って指定することができるようになっています。

たとえば、次のようなプロパティを持つクラス EzGradientButton が実装されていたとします。

imageMonochromeColor UIColor
imageMonochromeMode NSInteger

このような独自のプロパティは、Objective-C ソースコードから設定するのが一般的な方法だと思うのですが、そのためにわざわざ IBOutlet プロパティを用意したり viewDidLoad: メソッドにコードを書いたりするのは面倒です。

ましてや同じ UIViewController の派生クラスを使いながら、たとえば iPhone 用と iPad 用のように異なる Nib を使い分けるような場合、それぞれで違う設定が必要になったときには、コード内で条件分岐して調整しないといけません。

そんなときに User Defined Runtime Attributes を使うことで、コントロールのカスタムプロパティの設定を Interface Builder 内で設定できます。

設定方法

Xcode で NIB ファイルや Storyboard ファイルを開いたら、カスタムプロパティを選択したいコントロールを選択します。

そしてその Identity Inspector にある "User Defined Runtime Attributes" で、カスタムプロパティの値を設定します。

[ + ] 記号を押すと行が追加されるので、設定したいカスタムプロパティのプロパティ名 (Key Path) とデータ型 (Type) と、その値 (Value) を指定してあげることで、Interface Builder からの復帰時にその値が設定されるようになります。

 

データ型は Boolean, Number, String, Localized String, Point, Size, Rect, Range, Color, Nil から選択できるようになっていました。

Number 型を指定すると、NSInteger 型でも NSNumber* 型でも指定した値が設定されます。プロパティ名にドットは含められないので、構造体やクラスインスタンスを扱うプロパティは設定できない様子です。

実行タイミング

そして注意したいのは、このようにプロパティを設定したときの設定タイミングです。

User Defined Runtime Attributes で設定したプロパティは、NIB から復帰するときに呼ばれる initWithCoder: メソッドの実行が終わってから、awakeFromNib メソッドが実行されるより前に、セッターメソッドを通して設定されます。

 

セッターの実行が awakeFromNib の前ということは、他のコントロールが初期化されているとは限らず、他のコントロールにも値を反映する準備が整っていないタイミングで呼び出されます。

そのため、設定するカスタムプロパティが、設定された値を別のコントロールに表示するようなプロパティの場合、viewDidLoad でプロパティの値を画面に反映させるようなプログラムが必要になります。

プロパティ設定を間違えた場合

User Defined Runtime Attributes でのプロパティ設定を間違えると NIB のロード時に例外エラーが発生します。

たとえばプロパティ名 (Key Path) を間違えた場合は NSUnknownKeyException 例外が発生して、次のようなメッセージがログに表示されます。

[<EzSampleViewController 0x714d680> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key strings.

プロパティに対して異なった型の値を渡した場合は NSInvalidArgumentException 例外が発生して、プロパティの型にもよりますが、例えば NSNumber 型のプロパティに Rect を渡した場合は、次のようなエラーメッセージになりました。

-[NSConcreteValue integerValue]: unrecognized selector sent to instance 0x75bc950

このとき、いずれもプログラムの停止位置は "main.m" ファイルの UIApplicationMain 関数が実行されている行になるので、逆にこのようなエラーでアプリが落ちたときには、クラス設計と User Defined Runtime Attributes の設定に矛盾がないかを疑ってみるのもいいかもしれません。

 

User Defined Runtime Attributes の Localized String

ところで User Defined Runtime Attributes では、型として "Localized String" というものが指定できるようになっていました。

もしかして、NSLocalizedString を通した文字列をプロパティを設定してくれるのかと期待して試してみたのですけど、少なくとも Xcode 4.5.2 の時点ではそのようなことはなく、"String" 型を指定した時と同じ動作のようです。

 

NSLocalizedString といえば、実際には NSBundle の localizedStringForKey:value:table: メソッドを使って文字列をローカライズしているので、そのメソッドを差し替えて検証してみましたけど、このメソッドは呼ばれない様子です。

もしかしたら "Localizable.strings" ではない別のテーブルが呼ばれているのかもとも思ったのですけど、どうやら違うようでした。

 

そこで、セットされるプロパティに assert(false) を仕込んでもみたのですけど、足がかりになりそうなものを見つけられませんでした。

User Defined Runtime Attributes の値を String 型に変えてみても Localized String 型に変えてみても、少なくとも簡単に追える範囲では、メソッドの呼び出され方に特段違いはなさそうです。

UINib の instantiateWithOwner:options: メソッドが終わってから _NSSetUsingKeyValueSetter 関数が呼ばれた頃合いまでのどこかにもしかしたら手がかりとかあるんじゃないかと思うのですけど、それ以上の追い方が判りませんでした。

 

インターネットで調べてみても "Localized String は機能しない" みたいな結論ばかりなので、とりあえず今のところは、User Defined Runtime Attributes の Localized String 型は効果がないものと思っておくことにします。

[ もどる ]