封装自适应系统的ZCMAlert (UIAlertView和UIAlertController)

类ZCMAlert是一个用来展示alert框的封装类,iOS version >= 8.0 时自动使用UIAlertController,否则使用UIAlertView。

//
//  ZCMAlert.h
//  zcm
//
//  Created by cnstar on 4/4/2015.
//  Copyright (c) 2015年 cnstar. All rights reserved.
//

#import <Foundation/Foundation.h>

//系统版本号
#define SYSTEM_VERSION      [[[UIDevice currentDevice] systemVersion] floatValue]

@class ZCMAlertAction;

/**
 *  封装自适应系统的ZCMAlert (UIAlertView和UIAlertController)
 */
@interface ZCMAlert : NSObject
@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) NSString *message;
@property (nonatomic, readonly) NSArray *actions;

+(instancetype)alertWithTitle:(NSString *)title message:(NSString *)message;
-(void)show;
-(void)addAction:(ZCMAlertAction *)action;

@end

/**
 *  ZCMAlertStyle 只针对iOS 8.0及以上有效
 */
typedef NS_ENUM(NSInteger, ZCMAlertStyle) {
    ZCMAlertStyleDefault = 0,
    ZCMAlertStyleCancel,
    ZCMAlertStyleDestructive
};

typedef void(^ZCMAlertActionHandler)(ZCMAlertAction *);

/**
 *  ZCMAlertAction为alert view上的按钮动作类,一个按钮对应一个handler及一个title
 */
@interface ZCMAlertAction : NSObject
@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) ZCMAlertStyle style;
@property (copy) ZCMAlertActionHandler handler;

+ (instancetype)actionWithTitle:(NSString *)title style:(ZCMAlertStyle)style handler:(void (^)(ZCMAlertAction *action))handler;

@end
//
//  ZCMAlert.m
//  zcm
//
//  Created by cnstar on 4/4/2015.
//  Copyright (c) 2015年 cnstar. All rights reserved.
//

#import "ZCMAlert.h"

@interface ZCMAlert ()<UIAlertViewDelegate>
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *message;
@property (nonatomic, strong) NSMutableArray *mutableActions;
@property (nonatomic, strong) id holder;
@end

//做转换 ZCMAlertStyle ——> UIAlertActionStyle
static inline UIAlertActionStyle uistyle(ZCMAlertStyle style) {
    switch (style) {
        case ZCMAlertStyleCancel:
            return UIAlertActionStyleCancel;
            break;
        case ZCMAlertStyleDestructive:
            return UIAlertActionStyleDestructive;
            break;
            
        default:
            return UIAlertActionStyleDefault;
            break;
    }
}

@implementation ZCMAlert

-(void)dealloc {
    [self dismiss];
    self.mutableActions = nil;
    self.title = nil;
    self.message = nil;
}

-(void)dismiss {
    self.holder = nil;
}

+(instancetype)alertWithTitle:(NSString *)title message:(NSString *)message {
    return [[ZCMAlert alloc] initWithTitle:title message:message];
}

-(instancetype)initWithTitle:(NSString *)title message:(NSString *)message {
    if ((self = [super init])) {
        self.title = title;
        self.message = message;
    }
    
    return self;
}

/**
 *  show方法,show的时候再根据系统创建不同的alert。
 *  如果在init方法就创建alert的话,务必会声明一个id alert变量供addAction使用
 *  此时如果用户没有再调用show方法,此时UIAlertController会hold住self,导致不能跑到dealloc而内存泄漏
 */
-(void)show {
    if (SYSTEM_VERSION >= 8.0) {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:_title message:_message preferredStyle:UIAlertControllerStyleAlert];
        for (ZCMAlertAction *action in _mutableActions) {
            [alert addAction:[UIAlertAction actionWithTitle:action.title style:uistyle(action.style) handler:^(UIAlertAction *alertAction) {
                
                [self processForClicked:action];
                
                [self dismiss];
            }]];
        }
        
        UIViewController *viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
        if (!viewController) {
            for (UIWindow *window in [UIApplication sharedApplication].windows) {
                if (window.rootViewController) {
                    viewController = window.rootViewController;
                }
            }
        }
        
        [viewController presentViewController:alert animated:YES completion:nil];
    } else {
        self.holder = self; //在show之前先hold住self,以便能够正常回调action。原因是,如果不hold住当前self的话,show方法结束后self即被释放,此时UIAlertView的delegate=nil,导致不能正常回调
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:_title
                                                        message:_message
                                                       delegate:self
                                              cancelButtonTitle:nil
                                              otherButtonTitles:nil, nil];
        for (ZCMAlertAction *action in _mutableActions) {
            [alert addButtonWithTitle:action.title];
        }
        
        [alert show];
    }
}

-(void)processForClicked:(ZCMAlertAction *)action {
    ZCMAlertActionHandler block = action.handler; //此处不直接使用action.handler的原因是,如果多线程操作,刚进入if(action.handler){//此时正好被其他线程修改了action.handler=nil,那么下面调研action.handler()将会crash,因为block已经不存在了}。 ps:此处不会有多线程调用,只是为了时刻保持良好的认知      if (block) {
        block(action);
    }
}

-(NSArray *)actions {
    return _mutableActions;
}

-(void)addAction:(ZCMAlertAction *)action {
    if (!_mutableActions) {
        self.mutableActions = [NSMutableArray array];
    }
    
    if (action) {
        [_mutableActions addObject:action];
    }
}

#pragma mark - UIAlertView Delegate for iOS < 8.0
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex >= 0 && buttonIndex < _mutableActions.count) {
        ZCMAlertAction *action = [_mutableActions objectAtIndex:buttonIndex];
        [self processForClicked:action];
    }
    
    [self dismiss]; //必须调用,把holder置为nil, 否则最后self释放不了,会内存泄漏
}

- (void)alertViewCancel:(UIAlertView *)alertView {
    [self dismiss]; //必须调用,把holder置为nil, 否则最后self释放不了,会内存泄漏
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    [self dismiss]; //必须调用,把holder置为nil, 否则最后self释放不了,会内存泄漏
}

@end

#pragma mark - ZCMAlertAction

@interface ZCMAlertAction ()
@property (nonatomic, strong) NSString *title;
@property (nonatomic, assign) ZCMAlertStyle style;
@end

@implementation ZCMAlertAction

-(void)dealloc {
    self.title = nil;
    self.handler = nil;
}

+ (instancetype)actionWithTitle:(NSString *)title style:(ZCMAlertStyle)style handler:(void (^)(ZCMAlertAction *action))handler {
    return [[ZCMAlertAction alloc] initWithTitle:title style:style handler:handler];
}

- (instancetype)initWithTitle:(NSString *)title style:(ZCMAlertStyle)style handler:(void (^)(ZCMAlertAction *action))handler {
    if ((self = [super init])) {
        self.title = title;
        self.handler = handler;
        self.style = style;
    }
    
    return self;
}

@end

使用方法如下:

ZCMAlert *alert = [ZCMAlert alertWithTitle:@"提示" message:@"我是提示消息!"];
[alert addAction:[ZCMAlertAction actionWithTitle:@"知道了" style:ZCMAlertStyleDefault handler:^(ZCMAlertAction *action) {
            //NSLog(@"----------------------%@", action.title);
        }]];
[alert show];


ps:第一次开始写博客,故只会贴代码,记流水帐。难免有错误之处,望大家不吝赐教。谢谢!

你可能感兴趣的:(封装自适应系统的ZCMAlert (UIAlertView和UIAlertController))