JKAlertManager是基于UIAlertController封装的管理类,将UIAlertController分散的ActionBlcok集中到一个Blcok中,并且实现主动释放的Block,内存管理更安全,代码精简高效。
platform :ios, '8.0'
source 'https://github.com/CocoaPods/Specs.git'
pod 'JKAlertManager', '~> 1.2.1'
- JKAlertManager简介
- JKAlertManager迭代记录
- JKAlertManager用法
- JKAlertManager部分代码用意介绍
- 快速替换掉工程中所有的UIAlertView
- 封装UIAlertView、UIActionSheet的Block分类,替换代理方法
- 基于UIAlertController封装,兼容UIAlertControllerStyleAlert和UIAlertControllerStyleActionSheet
- UIAlertControllerStyleActionSheet类型兼容了iPad中的PopoverController
- 主动释放Block,内存管理更安全,有效规避Block循环引用
- 支持监听多个UITextField的文字内容变化,并通过Block回调监听结果
- 1.0.4,增加JKAlertView类用于全局替换UIAlertView。
- 1.1.0,JKAlertManager继承NSObject, 不再继承UIView。
JKAlertManager * manager = [[JKAlertManager alloc] initWithPreferredStyle:UIAlertControllerStyleAlert title:<#title#> message:<#nil#>];
[manager configueCancelTitle:<#@"取消"#> destructiveIndex:JKAlertDestructiveIndexNone otherTitles:<#array#>];
[manager showAlertFromController:self actionBlock:^(JKAlertManager *tempAlertManager, NSInteger actionIndex, NSString *actionTitle) {
if (actionIndex != tempAlertManager.cancelIndex) {
<#doSomething#>
}
}];
JKAlertManager * manager = [[JKAlertManager alloc]initWithPreferredStyle:UIAlertControllerStyleAlert title:self.dataArray[indexPath.row] message:self.dataArray[indexPath.row] ];
[manager configueCancelTitle:@"取消" destructiveIndex:1 otherTitles:@"其他按钮1",@"Destructive按钮",@"其他按钮2", nil];
[manager addTextFieldWithPlaceholder:@"请输入账号" secureTextEntry:NO ConfigurationHandler:nil textFieldTextChanged:^(UITextField *textField) {
NSLog(@"1 %@",textField.text);
}];
[manager addTextFieldWithPlaceholder:@"请输入密码" secureTextEntry:YES ConfigurationHandler:^(UITextField *textField) {
textField.clearsOnBeginEditing = YES;
} textFieldTextChanged:^(UITextField *textField) {
NSLog(@"2 %@",textField.text);
}];
[manager showAlertFromController:self actionBlock:^(JKAlertManager *tempAlertManager, NSInteger actionIndex, NSString *actionTitle) {
UITextField * userNameTextField = tempAlertManager.textFields[0];
UITextField * passwordTextField = tempAlertManager.textFields[1];
}];
如果App支持iPad,需调用configuePopoverControllerForActionSheetStyleWithSourceView:
适配iPad。
JKAlertManager * manager = [[JKAlertManager alloc]initWithPreferredStyle:UIAlertControllerStyleActionSheet title:@"title" message:@"massage" ];
[manager configueCancelTitle:nil destructiveIndex:0 otherTitles:@"Destructive按钮",@"其他按钮1",@"其他按钮2", nil];
[manager configuePopoverControllerForActionSheetStyleWithSourceView:cell sourceRect:cell.bounds popoverArrowDirection:UIPopoverArrowDirectionAny];
[manager showAlertFromController:self actionBlock:^(JKAlertManager *tempAlertManager, NSInteger actionIndex, NSString *actionTitle) {
self...
}];
-
之前封装的难点主要在于拆分
(NSString *)otherTitle, ...NS_REQUIRES_NIL_TERMINATION
,将otherTitle转换成数组,用法见Demo或者NS_REQUIRES_NIL_TERMINATION。但后来发现并没啥优越性,可以直接传NSArray。 解除Block循环引用的思路参考了AFNetworking
,即调用了Block后,再将Block = nil置空,即可解除循环引用,这种思路只适合调用一次的Block,多次调用的Block还是需要进行self强弱转换。 -
1.1.0
版中,JKAlertManager 改成继承 NSObject,被内部__JKAlertManagerPrivateHolder
类私有View强引用,JKAlertManager则会弱引用这个私有View。详情请看下面的代码:
@interface __JKAlertManagerPrivateHolder : UIView
@property (nonatomic, strong) JKAlertManager * alertManager;
@end
@implementation __JKAlertManagerPrivateHolder
@end
@interface JKAlertManager ()
@property (nonatomic, weak) __JKAlertManagerPrivateHolder * privateHolder;
@end
私有View privateHolder
在showAlertFromController:(UIViewController *)ViewController
方法中添加到控制器ViewController.view上,间接实现控制器对JKAlertManager的强引用。privateHolder
调用removeFromSuperView
来解除强引用,破坏原来的引用链,从而实现自释放。
- (void)showAlertFromController:(UIViewController *)controller actionBlock:(JKAlertActionBlock)actionBlock{
if (self.privateHolder) {
[self.privateHolder removeFromSuperview];
self.privateHolder = nil;
}
/// 私有类,强引用JKAlertManager
__JKAlertManagerPrivateHolder * privateHolder = [[__JKAlertManagerPrivateHolder alloc] initWithFrame:CGRectZero];
privateHolder.backgroundColor = [UIColor clearColor];
privateHolder.alertManager = self;
self.privateHolder = privateHolder;
[controller.view addSubview:privateHolder];
[controller presentViewController:self.alertController animated:YES completion:nil];
// 解除Block循环引用,释放内存
__weak typeof(self) weakSelf = self;
self.privateBlock = ^(NSInteger actionIndex, NSString * actionTitle){
__strong typeof(weakSelf) strongSelf = weakSelf;
if (actionBlock) actionBlock(strongSelf, actionIndex, actionTitle);
[[NSNotificationCenter defaultCenter]removeObserver:strongSelf];
strongSelf.alertController = nil;
strongSelf.otherTitles = nil;
/// 主动释放Block,解除循环引用
[strongSelf.privateHolder removeFromSuperview];
strongSelf.textFieldChangedBlockMutDict = nil;
strongSelf.privateBlock = nil;
};
}
如果项目中用到的UIAlertView相当多,同时又想全部换成UIAlertController,可用JKAlertView全局替换UIAlertView,大部分的UIAlertView都能替换,同时不用改原来的逻辑代码和代理方法。现在UIAlertView使用过程中并无异常,所以替不替换都无所谓。如果不需要全局替换UIAlertView,JKAlertView可删除掉,详情请查看工程中ReplaceViewController.m
文件中的代码。
-
#import "JKAlertView.h"
-
同时按
Command + Option + Shift + F
组合键`,用JKAlertView全局替换UIAlertView
- 一个UIAlertView或UIActionSheet对象对应一个Block,代码紧凑。
- 实现Block主动释放,规避Block循环引用,不会出现内存泄露。
如果控制器使用到多个UIAlertView或者UIActionSheet
,那么需要在多个地方创建对象并设置delegate和tag
,然后集中在一个代理方法中先根据tag区分对象,再区分buttonIndex
,这样写的代码比较分散,可读性不强。封装后的UIAlertView或者UIActionSheet
使用代码可读性更强,代码更紧凑。
不建议使用strong修饰alertView或者actionSheet属性,如果非得用属性,建议使用weak。
@property (nonatomic, strong)UIAlertView * alertView;
@property (nonatomic, strong)UIActionSheet * actionSheet;
self.alertView = [[UIAlertView alloc]initWithTitle:@"title" message:@"message" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"其他", nil];
[self.alertView showAlertViewWithActionBlock:^(UIAlertView *newAlertView, NSInteger buttonIndex) {
self...
}];
self.actionSheet = [[UIActionSheet alloc]initWithTitle:@"title" delegate:nil cancelButtonTitle:@"取消" destructiveButtonTitle:@"destructiveButtonTitle" otherButtonTitles:@"otherButtonTitles", nil];
[self.actionSheet showActionSheetInView:self.view completion:^(UIActionSheet *originalActionSheet, NSInteger buttonIndex) {
self...
}];