@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)