2011年11月12日土曜日

UIAlertView で block を使用する

iOS 5 時点の UIAlertView では delegate を設定し、完了後の処理を行っている。
@implementation MyClass
- (void)showAlert
{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Title"
                                                        message:@"Message"
                                                       delegate:self
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:@"Other Button", nil];
    [alertView show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    // Do something.
}
@end


これを GCD みたいに block を使用して処理したいと思ったので調べてみたところ、実装できたので公開する。


まず UIAlertView をサブクラス化し、インスタンス変数 _completionHandler を追加する。
buttonIndex は押下されたボタンを引数として渡すためのもの。
@interface MyAlertView : UIAlertView {
    void    (^_completionHandler)(NSInteger buttonIndex);
}
- (void)showWithCompletionHandler:(void(^)(NSInteger buttonIndex))_completionHandler;
@end


実装は次のとおり。
@implementation MyAlertView
- (void)showWithCompletionHandler:(void(^)(NSInteger buttonIndex))completionHandler
{
    // completionHandler を copy し、アラートを表示する。
    _completionHandler = [completionHandler copy];
    [self show];
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
    // アラートを閉じ、 _completionHandler を実行する。
    [super dismissWithClickedButtonIndex:buttonIndex animated:animated];
    _completionHandler(buttonIndex);
}
- (void)dealloc
{
    // _completionHandler を解放する。
    [_completionHandler release];
    [super dealloc];
}
@end


通常、 block をコピーする必要はないが、今回はその必要がある。
通常は、ブロックをコピー(または保持)する必要はありません。ブロックの宣言を含むスコープが破棄された後も、そのブロックを使用する可能性がある場合にのみ、ブロックのコピーを作成する必要があります。ブロックをコピーすると、ブロックはヒープに移動します。

ブロックプログラミングトピック.pdf (P.21)


MyAlertView を使用するとこうなる。
delegate は設定する必要がなくなったので nil としたが、もちろん必要なら設定する。
@implementation MyClass
- (void)showAlert
{
    MyAlertView *alertView = [[MyAlertView alloc] initWithTitle:@"Title"
                                                        message:@"Message"
                                                       delegate:nil
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:@"Other", nil];
    [alertView showWithCompletionHandler:^(NSInteger buttonIndex) {
        // Do something.
    }];
    [alertView autorelease];
}
@end


参考リンク
Blocks Programming Topic (iOS Developer Library)
ブロックプログラミングトピック.pdf (iOS Developer Library)