iOS Recipes - Matt Drance [19]
if (alertView == self.offlineAlertView) {
if ([buttonTitle isEqualToString:PRPOKTitle]) {
// ...
} else if ([buttonTitle isEqualToString:PRPCancelTitle]) {
// ...
}
} else if (alertView == self.serverErrorAlertView) {
if ([buttonTitle isEqualToString:PRPTryAgainTitle]) {
// ...
}
}
}
The delegate model is well-established in Cocoa Touch, but we can make the whole process much nicer with the help of blocks. We’re going to create a subclass, PRPAlertView, that streamlines the process of presenting alerts. Afterwords, you’ll have a reusable component that does in one method what used to require three or four. By using blocks, we can sidestep the delegate code and define the desired behavior for each button at creation time—not later, when the context of which-button-in-which-alert has been lost.
The subclass interface is very simple. We’ve avoided any initialization or delegates and used class methods that show alerts immediately. The first method takes a “cancel” or default button title, one other button title, and respective blocks to invoke when each button is tapped. We also define a simple block type (no return, no arguments) to make the code more readable.
PRPAlertView/PRPAlertView/PRPAlertView.h
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
cancelTitle:(NSString *)cancelTitle
cancelBlock:(PRPAlertBlock)cancelBlock
otherTitle:(NSString *)otherTitle
otherBlock:(PRPAlertBlock)otherBlock;
PRPAlertView/PRPAlertView/PRPAlertView.h
typedef void(^PRPAlertBlock)(void);
There’s also a simplified “show and do nothing” convenience method for when you just need to tell the user something but no response is needed.
PRPAlertView/PRPAlertView/PRPAlertView.h
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
buttonTitle:(NSString *)buttonTitle;
The implementation is straightforward: both of these convenience methods create, show, and autorelease an alert using our newly defined ‑initWithTitle:... method listed next. This method saves the passed blocks and button titles into copy-style properties for comparison later. It also acts as its own delegate—if one or more of the handler blocks is actually passed.
PRPAlertView/PRPAlertView/PRPAlertView.m
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
cancelTitle:(NSString *)cancelTitle
cancelBlock:(PRPAlertBlock)cancelBlk
otherTitle:(NSString *)otherTitle
otherBlock:(PRPAlertBlock)otherBlk {
[[[[self alloc] initWithTitle:title message:message
cancelTitle:cancelTitle cancelBlock:cancelBlk
otherTitle:otherTitle otherBlock:otherBlk]
autorelease] show];
}
PRPAlertView/PRPAlertView/PRPAlertView.m
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
cancelTitle:(NSString *)cancelTitle
cancelBlock:(PRPAlertBlock)cancelBlk
otherTitle:(NSString *)otherTitle
otherBlock:(PRPAlertBlock)otherBlk {
if ((self = [super initWithTitle:title
message:message
delegate:self
cancelButtonTitle:cancelTitle
otherButtonTitles:otherTitle, nil])) {
if (cancelBlk == nil && otherBlk == nil) {
self.delegate = nil;
}
self.cancelButtonTitle = cancelTitle;
self.otherButtonTitle = otherTitle;
self.cancelBlock = cancelBlk;
self.otherBlock = otherBlk;
}
return self;
}
The init method, as well as the properties, are tucked away in a private class extension to simplify the interface defined in the header file. This increases readability and encourages consumers to use only the convenience methods, which is the easiest way to use the class.
PRPAlertView/PRPAlertView/PRPAlertView.m
@interface PRPAlertView ()
@property (nonatomic, copy) PRPAlertBlock cancelBlock;
@property (nonatomic, copy) PRPAlertBlock otherBlock;
@property (nonatomic, copy) NSString *cancelButtonTitle;
@property (nonatomic, copy) NSString *otherButtonTitle;
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
cancelTitle:(NSString *)cancelTitle
cancelBlock:(PRPAlertBlock)cancelBlock
otherTitle:(NSString *)otherTitle
otherBlock:(PRPAlertBlock)otherBlock;
@end
The