こんにちは!
共同開発部で日々コードと戯れております、福井です。
iOS 4.0 から追加された関数オブジェクト的なものである Blocks をご存知でしょうか。この Blocks のおかげで UIView のアニメーションは劇的に書きやすくなりました。もう本当に Blocks って素晴らしいですね。
アニメーション?
違う!違うよ!根本的に違うよ!
Blocks でやりたいのはアニメーションじゃないよ!
おれは Blocks でコールバックを書きたいんだ!
ということでさっそく書いてみました。
今回は MyPickerView という UIPickerView をちょっと拡張したものを作ってみます。
MyPickerView の要件はこちら
MyPickerView は以下のような感じになります。
interface 部を見てみましょう。
@interface MyPickerView : UIView <UIPickerViewDataSource, UIPickerViewDelegate> { UIToolbar *toolbar; UIPickerView *picker; void (^completion)(NSString *selected); NSArray *items; } - (void)setSelectedRow:(NSInteger)row; - (NSInteger)selectedRow; - (void)setItems:(NSArray *)array; - (void)showInWindow:(UIWindow *)window completion:(void (^)(NSString *selected))aCompletion; - (void)hide; @end
void (^completion)(NSString *selected) という見慣れない変数がコールバックです。
誰もが気持ち悪いと感じますね。そこも Blocks のいいところです。
この Blocks は返り値が void 型で NSString 型の引数をひとつ持ちます。
実際には、showInWindow:completion: で completion にコールバックをセットし、終了がタップされたときのイベントで選択されているものを引数にセットして呼び出します。
その他はだいたい読んだ通りです。
UIPickerViewDelegate を実装していることからお察しの通り picker は items に格納されている NSString を表示します。
では、コールバックを使って先ほどの参考画像で背景にうすらぼんやり見えているボタンのタイトルを変更してみましょう。
うすらぼんやりなボタンにセットしたタップイベントがこちら
- (void)tapped:(id)sender { UIButton *tappedButton = (UIButton *)sender; [picker showInWindow:[self.view window] completion:^(NSString *selected){ [tappedButton setTitle:selected forState:UIControlStateNormal]; }]; }
これだけのコードでボタンのタイトルが変更されます。
従来のように delegate を使って同じことを実現するとターゲットになるオブジェクトを受け渡すなどの処理が必要になりますが、Blocks ではクロージャが使えるのでそれらのコードが不要になり、より簡潔に記述できることがわかります。
また、この実装だと仮にボタンが増えても新しく追加したボタンに tapped: をタップイベントに設定してやるだけで対応できそうです。
このサンプルではさほどありがたみがわかりませんが、Blocks を使うとなんやらいい感じにラクができそうです。Blocks は記述以外にもメモリの管理なんかも少しややこしいのでとっつきにくいイメージがありますが、とても強力な機能なので是非試してみてください。
MyPickerView の全実装コードはこちらです。