UIAlertView with blocks, revisited

One issue with my UIAlertView with blocks class is that it only supports willDismiss, not any other delegate methods supported by UIAlertViewDelegate. At the time I wrote it, that was all I needed, but more recently, my requirements changed. (Hint: if you’re presenting a UIActionSheet from within a willDismiss delegate method call, bad things will probably happen, as of iOS 4.2).

So I’ve updated it to support all the other UIAlertViewDelegate methods. It’s pretty straightforward.

First we make typedefs for each type of block, one corresponding to each UIAlertViewDelegate method.

typedef void (^WillPresentBlock)(void);
typedef void (^DidPresentBlock)(void);
typedef void (^DidCancelBlock)(void);
typedef void (^ClickedButtonBlock)(NSInteger);
typedef void (^WillDismissBlock)(NSInteger);
typedef void (^DidDismissBlock)(NSInteger);

Then we need an instance variable, and a property for each.

@interface ZPAlertView : UIAlertView
{
    WillPresentBlock willPresentBlock;
    DidPresentBlock didPresentBlock;
    DidCancelBlock didCancelBlock;
    ClickedButtonBlock clickedButtonBlock;
    WillDismissBlock willDismissBlock;
    DidDismissBlock didDismissBlock;
}
@property (nonatomic, copy) WillPresentBlock willPresentBlock;
@property (nonatomic, copy) DidPresentBlock didPresentBlock;
@property (nonatomic, copy) DidCancelBlock didCancelBlock;
@property (nonatomic, copy) ClickedButtonBlock clickedButtonBlock;
@property (nonatomic, copy) WillDismissBlock willDismissBlock;
@property (nonatomic, copy) DidDismissBlock didDismissBlock;
@end

Note that the properties are set for copy. Retaining a block doesn’t work as you’d expect; we need to make a copy.

We synthesize our properties. No need for custom stuff here.

@synthesize willPresentBlock;
@synthesize didPresentBlock;
@synthesize didCancelBlock;
@synthesize clickedButtonBlock;
@synthesize willDismissBlock;
@synthesize didDismissBlock;

Since we’ve made our properties copy, we need to make sure we release them in dealloc.

- (void) dealloc
{
    [willPresentBlock release];
    [didPresentBlock release];
    [didCancelBlock release];
    [clickedButtonBlock release];
    [willDismissBlock release];
    [didDismissBlock release];

    [super dealloc];
}

The real magic is in our overridden show method, which doesn’t look very much like magic at all:

- (void) show
{
    self.delegate = self;
    [super show];
}

Finally we have the actual delegate method calls, each of which call the appropriate block. That’s it.

- (void) willPresentAlertView:(UIAlertView*)alertView
{
    if( willPresentBlock != nil ) {
        willPresentBlock();
    }
}

- (void) didPresentAlertView:(UIAlertView*)alertView
{
    if( didPresentBlock != nil ) {
        didPresentBlock();
    }
}

- (void) alertViewCancel:(UIAlertView *)alertView
{
    if( didCancelBlock != nil ) {
        didCancelBlock();
    }
}

- (void) alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if( clickedButtonBlock != nil ) {
        clickedButtonBlock(buttonIndex);
    }
}

- (void) alertView:(UIAlertView*)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if( willDismissBlock != nil ) {
        willDismissBlock(buttonIndex);
    }
}

- (void) alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if( didDismissBlock != nil ) {
        didDismissBlock(buttonIndex);
    }
}

The client code would look like this:

- (void) doLengthyOperationPrompt
{
    enum {
        ButtonIndexCancel = 0,
        ButtonIndexDoItNow,
        ButtonIndexDoItLater
    };

    ZPAlertView* anAlert = [[ZPAlertView alloc] initWithTitle:@"Warning!"
        message:@"Would you like to perform a really lengthy operation?"
        delegate:nil
        cancelButtonTitle:@"Nope"
        otherButtonTitles:@"Yeah, sure", @"Meh, maybe later", nil];
    anAlert.didDismissBlock = ^(NSInteger buttonIndex){
        switch( buttonIndex ) {
            case ButtonIndexDoItNow:
                [self performLengthyOperation];
            break;
            case ButtonIndexDoItLater:
                [self scheduleLengthyOperationForLater];
            break;
        }
    };
    [anAlert show];
    [anAlert release];
}

I’ll try to put the full source and and example project up somewhere, soon. Any and all feedback is welcome.

Update: It’s now up on github, here.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.