为UIAlertView添加block支持

虽然从iOS8.0开始大家都开始用UIAlertController来做警示框了,但是市面上APP大部分还都是支持iOS7的,那么不可避免的就要面对UIAlertView处理的问题。苹果官方提供了UIAlertView的delegate代理回调的方式,但是,却经常因多重代理或者多重继承代理处理不好引起delegate无效的问题。所以,让UIAlertView支持block回调可以让程序的可读性更高,开发也更方便!下面附上代码,仅供参考。

 

.h文件

//
//  UIAlertView+BlocksKit.h
//  BlocksKit
//

#import <UIKit/UIKit.h>

/** UIAlertView without delegates!

 This set of extensions and convenience classes allows
 for an instance of UIAlertView without the implementation
 of a delegate.  Any time you instantiate a UIAlertView
 using the methods here, you must add buttons using
 addButtonWithTitle:handler:otherwise nothing will happen.

 A typical invocation will go like this:
     UIAlertView *testView = [UIAlertView alertViewWithTitle:@"Application Alert" message:@"This app will explode in 42 seconds."];
     [testView setCancelButtonWithTitle:@"Oh No!" handler:^{ NSLog(@"Boom!"); }];
     [testView show];

 A more traditional, and more useful, modal dialog looks like so:
    UIAlertView *testView = [UIAlertView alertViewWithTitle:@"Very important!" message:@"Do you like chocolate?"];
    [testView addButtonWithTitle:@"Yes" handler:^{ NSLog(@"Yay!"); }];
    [testView addButtonWithTitle:@"No" handler:^{ NSLog(@"We hate you."); }];
    [testView show];

 Includes code by the following:

 - [Landon Fuller](http://landonf.bikemonkey.org), "Using Blocks".
 - [Peter Steinberger](https://github.com/steipete)
 - [Zach Waldowski](https://github.com/zwaldowski)

 @warning UIAlertView is only available on a platform with UIKit.
 */
@interface UIAlertView (BlocksKit)

///-----------------------------------
/// @name Creating alert views
///-----------------------------------

/** Creates and shows a new alert view with only a title, message, and cancel button.
 
 @param title The title of the alert view.
 @param message The message content of the alert.
 @param cancelButtonTitle The title of the cancel button. If both cancelButtonTitle and otherButtonTitles are empty or nil, defaults to a
 @param otherButtonTitles Titles of additional buttons to add to the receiver.
 @param block A block of code to be fired on the dismissal of the alert view.
 
 @return The UIAlertView.
 */
+ (UIAlertView*)bk_showAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles handler:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))block;

/** Creates and returns a new alert view with only a title and cancel button.

 @param title The title of the alert view.
 @return A newly created alert view.
 */
+ (id)bk_alertViewWithTitle:(NSString *)title;

/** Creates and returns a new alert view with only a title, message, and cancel button.

 @param title The title of the alert view.
 @param message The message content of the alert.
 @return A newly created alert view.
 */
+ (id)bk_alertViewWithTitle:(NSString *)title message:(NSString *)message;

/** Returns a configured alert view with only a title, message, and cancel button.
 
 @param title The title of the alert view.
 @param message The message content of the alert.
 @return An instantiated alert view.
 */
- (id)bk_initWithTitle:(NSString *)title message:(NSString *)message NS_REPLACES_RECEIVER;

///-----------------------------------
/// @name Adding buttons
///-----------------------------------

/** Add a new button with an associated code block.
 
 @param title The text of the button.
 @param block A block of code.
 */
- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block;

/** Set the title and trigger of the cancel button.
 
 @param title The text of the button.
 @param block A block of code.
 */
- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block;

///-----------------------------------
/// @name Altering actions
///-----------------------------------

/** Sets the block that is to be fired when a button is pressed.
 
 @param block A code block, or nil to set no response.
 @param index The index of a button already added to the action sheet.
 */
- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index;

/** The block that is to be fired when a button is pressed.
 
 @param index The index of the button already added to the alert view.
 @return A code block, or nil if no block yet assigned.
 */
- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index;

/** The block to be fired when the action sheet is dismissed with the cancel
 button.

 Contrary to setCancelButtonWithTitle:handler:, you can set this
 property multiple times but multiple cancel buttons will
 not be generated.
 */
@property (nonatomic, copy, setter = bk_setCancelBlock:) void (^bk_cancelBlock)(void);

/** The block to be fired before the alert view will show. */
@property (nonatomic, copy, setter = bk_setWillShowBlock:) void (^bk_willShowBlock)(UIAlertView *alertView);

/** The block to be fired when the alert view shows. */
@property (nonatomic, copy, setter = bk_setDidShowBlock:) void (^bk_didShowBlock)(UIAlertView *alertView);

/** The block to be fired before the alert view will dismiss. */
@property (nonatomic, copy, setter = bk_setWillDismissBlock:) void (^bk_willDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex);

/** The block to be fired after the alert view dismisses. */
@property (nonatomic, copy, setter = bk_setDidDismissBlock:) void (^bk_didDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex);

/** The block to be fired to determine whether the first non-cancel should be enabled */
@property (nonatomic, copy, setter = bk_SetShouldEnableFirstOtherButtonBlock:) BOOL (^bk_shouldEnableFirstOtherButtonBlock)(UIAlertView *alertView) NS_AVAILABLE_IOS(5_0);

@end

 

.m文件

//
//  UIAlertView+BlocksKit.m
//  BlocksKit
//
#import "UIAlertView+BlocksKit.h"

#pragma mark Delegate

@interface A2DynamicUIAlertViewDelegate : A2DynamicDelegate <UIAlertViewDelegate>

@end

@implementation A2DynamicUIAlertViewDelegate

- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
    BOOL should = YES;
    
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(alertViewShouldEnableFirstOtherButton:)])
        should &= [realDelegate alertViewShouldEnableFirstOtherButton:alertView];
    
    BOOL (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd];
    if (block)
        should &= block(alertView);
    
    return should;
}

- (void)alertViewCancel:(UIAlertView *)alertView
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(alertViewCancel:)])
        [realDelegate alertViewCancel:alertView];
    
    id key = @(alertView.cancelButtonIndex);
    void (^cancelBlock)(void) = (self.handlers)[key];
    if (cancelBlock)
        cancelBlock();
}

- (void)willPresentAlertView:(UIAlertView *)alertView
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(willPresentAlertView:)])
        [realDelegate willPresentAlertView:alertView];
    
    void (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(alertView);
}

- (void)didPresentAlertView:(UIAlertView *)alertView
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(didPresentAlertView:)])
        [realDelegate didPresentAlertView:alertView];
    
    void (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(alertView);
}

- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:willDismissWithButtonIndex:)])
        [realDelegate alertView:alertView willDismissWithButtonIndex:buttonIndex];
    
    void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(alertView, buttonIndex);
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:didDismissWithButtonIndex:)])
        [realDelegate alertView:alertView didDismissWithButtonIndex:buttonIndex];
    
    void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(alertView, buttonIndex);
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    id realDelegate = self.realDelegate;
    if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:clickedButtonAtIndex:)])
        [realDelegate alertView:alertView clickedButtonAtIndex:buttonIndex];
    
    void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd];
    if (block)
        block(alertView, buttonIndex);
    
    id key = @(buttonIndex);
    void (^buttonBlock)(void) = (self.handlers)[key];
    if (buttonBlock)
        buttonBlock();
}

@end

#pragma mark - Category

@implementation UIAlertView (BlocksKit)

@dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock;

+ (void)load
{
    @autoreleasepool {
        [self bk_registerDynamicDelegate];
        [self bk_linkDelegateMethods:@{
            @"bk_willShowBlock": @"willPresentAlertView:",
            @"bk_didShowBlock": @"didPresentAlertView:",
            @"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:",
            @"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:",
            @"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:"
        }];
    }
}

#pragma mark Convenience

+ (UIAlertView*)bk_showAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles handler:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))block
{
    // If no buttons were specified, cancel button becomes "Dismiss"
    if (!cancelButtonTitle.length && !otherButtonTitles.count)
        cancelButtonTitle = NSLocalizedString(@"Dismiss", nil);
    
    UIAlertView *alertView = [[[self class] alloc] initWithTitle:title message:message delegate:self.bk_dynamicDelegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];

    // Set other buttons
    [otherButtonTitles enumerateObjectsUsingBlock:^(NSString *button, NSUInteger idx, BOOL *stop) {
        [alertView addButtonWithTitle:button];
    }];

    // Set `didDismissBlock`
    if (block) alertView.bk_didDismissBlock = block;
    
    // Show alert view
    [alertView show];
    
    return alertView;
}

#pragma mark Initializers

+ (id)bk_alertViewWithTitle:(NSString *)title
{
    return [self bk_alertViewWithTitle:title message:nil];
}

+ (id)bk_alertViewWithTitle:(NSString *)title message:(NSString *)message
{
    return [[[self class] alloc] bk_initWithTitle:title message:message];
}

- (id)bk_initWithTitle:(NSString *)title message:(NSString *)message
{
    self = [self initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
    if (!self) return nil;

    self.delegate = self.bk_dynamicDelegate;

    return self;
}

#pragma Actions

- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block
{
    NSAssert(title.length, @"A button without a title cannot be added to the alert view.");
    NSInteger index = [self addButtonWithTitle:title];
    [self bk_setHandler:block forButtonAtIndex:index];
    return index;
}

- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block
{
    if (!title.length)
        title = NSLocalizedString(@"Cancel", nil);
    NSInteger cancelButtonIndex = [self addButtonWithTitle:title];
    self.cancelButtonIndex = cancelButtonIndex;
    [self bk_setHandler:block forButtonAtIndex:cancelButtonIndex];
    return cancelButtonIndex;
}

#pragma mark Properties

- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index
{
    id key = @(index);
    if (block)
        [self.bk_dynamicDelegate handlers][key] = [block copy];
    else
        [[self.bk_dynamicDelegate handlers] removeObjectForKey:key];
}

- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index
{
    return [self.bk_dynamicDelegate handlers][@(index)];
}

- (void (^)(void))bk_cancelBlock
{
    return [self bk_handlerForButtonAtIndex:self.cancelButtonIndex];
}

- (void)bk_setCancelBlock:(void (^)(void))block
{
    if (block && self.cancelButtonIndex == -1) {
        [self bk_setCancelButtonWithTitle:nil handler:block];
        return;
    }
    
    [self bk_setHandler:block forButtonAtIndex:self.cancelButtonIndex];
}

@end

posted @ 2016-05-31 14:05  小固编程  阅读(324)  评论(0)    收藏  举报