Stack's and Guillermo Ortega's answer is probably what you would use with a couple of UIAlertView but not for ten. I use to use BlocksKit which is kind of the same as Lambda stuff which is what soul suggested. That is a good option too, although if you have too many nested blocks you will start seeing the demerits of it (Aside from the fact you will be relying in another library).
The usual way of handling several stuff would be to have a handler object. (
@interface MyAlertViewDelegate : NSObject <UIAlertViewDelegate> @end
) make that object the delegate of the alert view and make sure the object is alive at least until the alert view is dismissed.
This will certainly work, but could be too much work...
What follows is what I came up with; IMO it is simpler and there is no need of any thirdParty library, or an ivar per UIAlertView. Just one extra object (@property (nonatomic, strong) NSArray *modalActions
) to store the actions the current UIAlertView will cause to perform
Showing an UIAlertView and reacting accordingly
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle
message:@"Blah blah"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:b1, b2, b3, nil];
// Add one selector/action per button at the proper index
self.modalActions = @[
[NSNull null], // Because indexes of UIAlertView buttons start at 1
NSStringFromSelector(@selector(actionForAlertViewButton1)),
NSStringFromSelector(@selector(actionForAlertViewButton2)),
NSStringFromSelector(@selector(actionForAlertViewButton3))];
[alertView show];
The delegate method:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.cancelButtonIndex != buttonIndex) {
[self performModalActionAtIndex:buttonIndex];
}
}
The part that actually performs the action:
- (void)performModalActionAtIndex:(NSInteger)index
{
if (-1 < index && index < self.modalActions.count &&
[self.modalActions[index] isKindOfClass:[NSString class]]) {
SEL action = NSSelectorFromString(self.modalActions[index]);
NSLog(@"action: %@", self.modalActions[index]);
if ([self respondsToSelector:action]) {
// There is a situation with performSelector: in ARC.
// http://stackoverflow.com/questions/7017281/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:action];
#pragma clang diagnostic pop
}
self.modalActions = nil;
}
Reusable for UIActionSheets too
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self
cancelButtonTitle:cancelButton
destructiveButtonTitle:nil
otherButtonTitles:button1, button2, button3, nil];
// Similarly, add one action per button at the proper index
self.modalActions = @[
NSStringFromSelector(@selector(actionForActionSheetButton1)),
NSStringFromSelector(@selector(actionForActionSheetButton2)),
NSStringFromSelector(@selector(actionForActionSheetButton3))];
The delegate method:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (actionSheet.cancelButtonIndex != buttonIndex) {
[self performModalActionAtIndex:buttonIndex];
}
}
Why this works:
This works because of two reasons:
First, I never present two UIAlertView that have a delegate at the same time. (IMO you should't, it doesn't look good). Second, because in my case (as 90% of the cases) the target of the actions is always the same object (in this case: self
). Even if you don't meet above conditions you can even use this approach with some modifications:
If you show two or more UIAlerViews or UIActionSheets at the same time (possible in the iPad) Use a dictionary with to store one array of actions associated with a certain UIAlertView/UIActionSheet.
If the target of the actions is not self
, they you need to store pairs (target and the action) in the array. (Something to simulate UIButtons addTarget:action:...
).
In either case, for storing the target and/or UIActionSheet/UIAlertView [NSValue valueWithNonretainedObject:]
should become handy :)