IBOutletCollection を使用する : Objective-C プログラミング

PROGRAM


IBOutletCollection を使用する

Xcode の Interface Builder では、IBOutletCollection を使うことで、複数のコントロールをひとつの配列変数に関連付けて管理できます。

コードでの定義方法は、IBOutlet の代わりに IBOutletCollection(ControlType) を使用して、変数の型は NSArray 型で宣言します。

@property (nonatomic,readwrite,strong) IBOutletCollection(UILabel*) NSArray* labels;

例えばこのようにすることで、UILabel 型のコントロールを関連付けるための配列 labels が定義できました。

これで、この変数に Interface Builder から複数のコントロールに対して、関連付けの線を引っ張って行くことが可能です。

型として UILabel クラスを指定しているので、それから派生したクラスであれば、それらを混在して関連付けられます。

 

IBOutletCollection を使用する際の留意点

プロパティは strong で指定する

ARC のコードで IBOutlet を使う場合、Interface Builder のコントロールを weak で関連付けることが多いと思いますが、この IBOutletCollection の変数は必ず strong を使用する必要があるようでした。

そうしないと、実行時にいきなり解放されて nil になっているので注意が必要です。

 

iOS 5 以前では中身が解放されないように注意

iOS 5 以前で Storyboard の UIView より外に配置したコントロールを IBOutletCollection に連結するときも注意が必要です。

このようなコントロールを IBOutletCollection のプロパティに連結すると、IBOutletCollection は strong 指定になっていても、iOS 5 以前で View の準備が整った段階で、設定されたコントロールが解放されてしまっているようでした。

IBOutletCollection を指定した NSArray は weak ではないため、いっけんするとちゃんとインスタンスが保存されているように見えるのですが、そのまま利用しようとすると "deallocated instance" の例外で落ちてしまいます。

これを防ぐためには UIViewController やそれに所属する UIView などのどこかで、使用するインスタンスをキープしておく必要があります。

 

IBOutletCollection を意図した順番に並び替える

IBOutletCollection を使う上でのもうひとつの大きな注意点として、IBOutletCollection に関連付けられたオブジェクトの順番です。

IBOutletCollection の配列に連結したコントロールは、それを連結した順番に関わらず、システムの都合で適当に並べられてしまう様子です。そのため、意図した順番で処理をしたい場合には、プログラム側で並び替える必要があります。

 

並び替えは、NSArray の場合は sortedArrayUsingComparator: メソッドを使って簡単に行えます。

 

たとえば、コントロールに予め Tag 番号を設定しておいて、その順番に並び替えたい場合は次のようにします。

NSArray* labels = [self.labels sortedArrayUsingComparator:

^NSComparisonResult(UILabel* obj1, UILabel* obj2)

{

NSComparisonResult result;

 

if (obj1.tag == obj2.tag)

{

result = NSOrderedSame;

}

else

{

result = (obj1.tag < obj2.tag ? NSOrderedAscending : NSOrderedDescending);

}

return result;

}

];

このように sortedArrayUsingComparator: の引数に、NSComparisonResult を戻り値とする Blocks メソッドを渡します。

配列から 2 つのオブジェクトが順次渡されてくるので、それらを使って大小関係を返してあげるコードを書いておけば、後は自動的にその大小関係に従って並び替えられた配列を取得することができます。

 

同じように、例えば X 座標順、つまり左から順にオブジェクトを取得したい場合には、次のようにします。

NSArray* labels = [self.labels sortedArrayUsingComparator:

^NSComparisonResult(UILabel* obj1, UILabel* obj2)

{

NSComparisonResult result;

 

CGFloat x1 = obj1.frame.origin.x;

CGFloat x2 = obj2.frame.origin.x;

 

if (x1 == x2)

{

result = NSOrderedSame;

}

else

{

result = (x1 < x2 ? NSOrderedAscending : NSOrderedDescending);

}

return result;

}

];

これで、左から順番にコントロールを取得することができました。

 

IBOutletCollection はこのような感じで、都合に合わせて sortedArrayUsingComparator: に渡す Blocks メソッドを記載して、関連付けられた複数のコントロールを上手に制御する必要があります。

今回お話しした サンプルコード を GitHub に用意したので、必要に応じてそちらも参考にしてみてください。

[ もどる ]