iOS开发 UIAlertView与UIActionSheet替换方案之SDAlertView与SDActionSheet
由于在iOS开发中,项目中还在使用UIAlertView与UIActionSheet,由于这两个类在iOS开始废弃
UIKIT_EXTERN API_DEPRECATED(“UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead”, ios(2.0, 9.0)) API_UNAVAILABLE(tvos) API_UNAVAILABLE(visionos, watchos) NS_SWIFT_UI_ACTOR
需要更换成UIAlertController。UIAlertView与UIActionSheet都更换成UIAlertController,一个一个文件更改代码不太合适,更改较多,所以考虑自定义类封装UIAlertController使用。
UIAlertView与UIActionSheet
一般在UIAlertView中,我们会使用initWithTitle方法进行初始化
- (instancetype)initWithTitle:(nullable NSString )title message:(nullable NSString )message delegate:(nullable id //)delegate cancelButtonTitle:(nullable NSString *)cancelButtonTitle otherButtonTitles:(nullable NSString *)otherButtonTitles, … NS_REQUIRES_NIL_TERMINATION;
然后使用其代理方法UIAlertViewDelegate的方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex API_DEPRECATED(“Use UIAlertController instead.”, ios(2.0, 9.0)) API_UNAVAILABLE(visionos, watchos);
在该方法处理点击事件。
UIActionSheet和UIAlertView使用方式类似。
封装UIAlertController
在工程中直接替换会更改太多,那这时候我这边考虑封装UIAlertController进行,封装后的类SDAlertView
虽然类名SDAlertView,但是其实继承NSObject,其中封装UIAlertController进行使用。
- 初始化方法initWithTitle
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
- 创建delegate及新增方法
@protocol SDAlertViewDelegate <NSObject>
- (void)alertView:(SDAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
@end
- show方法
通过获取keyWindow的rootViewController来展示UIAlertController
完整代码如下
SDAlertView.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UIAlertAction+SDIndex.h"
@protocol SDAlertViewDelegate;
@interface SDAlertView : NSObject
@property (nonatomic, weak) id<SDAlertViewDelegate>delegate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *message; // secondary explanation text
@property (nonatomic) NSInteger tag; // default is 0
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
- (NSString *)buttonTitleAtIndex:(NSInteger)buttonIndex;
- (void)show;
@end
@protocol SDAlertViewDelegate <NSObject>
- (void)alertView:(SDAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
@end
SDAlertView.m
#import "SDAlertView.h"
@interface SDAlertView ()
@property (nonatomic, copy) NSString *cancelButtonTitle;
@property (nonatomic, copy) NSArray *otherButtonTitleList;
@property (nonatomic, copy) NSArray *allBtnTitleList;
@property (nonatomic, strong) UIAlertController *alertController;
@end
@implementation SDAlertView
- (instancetype)init
{
self = [super init];
if (self) {
if (sd_alertSheets == nil) {
sd_alertSheets = [NSMutableArray arrayWithCapacity:0];
}
}
return self;
}
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION; {
self = [self init];
if (self) {
self.delegate = delegate;
self.title = title;
self.message = message;
self.cancelButtonTitle = cancelButtonTitle;
NSMutableArray *tmpTitleList = [NSMutableArray arrayWithCapacity:0];
if (otherButtonTitles) {
[tmpTitleList addObject:otherButtonTitles];
// 后面的不定参数,可以使用va_list来获取
//va_list 是一个指针变量 本质上是一个指针
va_list arg_lists;
//初始化 va_list
va_start(arg_lists, otherButtonTitles);
NSString * obj = nil;
//va_arg(arg_lists, NSString*)用户来获取值
while((obj=va_arg(arg_lists, NSString*))){
if (obj && obj.length > 0) {
[tmpTitleList addObject:[NSString stringWithFormat:@"%@", obj]];
}
};
//结束参数获取
va_end(arg_lists);
}
self.otherButtonTitleList = [NSArray arrayWithArray:tmpTitleList];
// 所有按钮title
NSMutableArray *tmpAllTitles = [NSMutableArray arrayWithCapacity:0];
if (self.cancelButtonTitle && [self.cancelButtonTitle isKindOfClass:[NSString class]] && self.cancelButtonTitle.length) {
[tmpAllTitles addObject:self.cancelButtonTitle];
}
[tmpAllTitles addObjectsFromArray:self.otherButtonTitleList];
self.allBtnTitleList = tmpAllTitles;
// 初始化AlertController
[self setupAlertView];
// 采用这种方式,当点击的时候,将其从列表移除,防止在调用handleAlertAction时候self被释放掉了。
// 调用handleAlertAction,将其从数组中移除,移除后自动调用dealloc方法
[sd_alertSheets addObject:self];
NSLog(@"sd_alertSheets:%@", sd_alertSheets);
}
return self;
}
- (void)setupAlertView {
self.alertController = [UIAlertController alertControllerWithTitle:self.title
message:self.message
preferredStyle:UIAlertControllerStyleAlert];
__block BOOL hasCancelBtn = NO;
__weak typeof(self) weakSelf = self;
if (self.cancelButtonTitle && [self.cancelButtonTitle isKindOfClass:[NSString class]] && self.cancelButtonTitle.length) {
hasCancelBtn = true;
__block int buttonIndex = 0;
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:self.cancelButtonTitle
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
[weakSelf handleAlertAction:action buttonIndex:action.actionIndex];
}];
cancelAction.actionIndex = buttonIndex;
[self.alertController addAction:cancelAction];
}
for (int i = 0; i < self.otherButtonTitleList.count; i++) {
__block int buttonIndex = i + (hasCancelBtn?1:0);
NSString *title = [self.otherButtonTitleList objectAtIndex:i];
UIAlertAction *alertAction = [UIAlertAction actionWithTitle:title
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[weakSelf handleAlertAction:action buttonIndex:action.actionIndex];
}];
alertAction.actionIndex = buttonIndex;
[self.alertController addAction:alertAction];
}
}
- (NSString *)buttonTitleAtIndex:(NSInteger)buttonIndex {
if (self.allBtnTitleList && (buttonIndex < self.allBtnTitleList.count)) {
NSString *title = [self.allBtnTitleList objectAtIndex:buttonIndex];
return title;
}
return nil;
}
- (void)show {
if (!self.alertController) {
return;
}
UIViewController *topViewController = UIApplication sharedApplication].keyWindow.rootViewController;
[topViewController presentViewController:self.alertController animated:YES completion:^{
DLog(@"SDAlertView did show completion");
}];
}
- (void)handleAlertAction:(UIAlertAction *)alertAction buttonIndex:(NSInteger)buttonIndex {
DLog(@"handleAlertAction alertAction:%@ buttonIndex:%ld", alertAction, buttonIndex);
if (self.delegate && [self.delegate respondsToSelector:@selector(alertView:clickedButtonAtIndex:)]) {
[self.delegate alertView:self clickedButtonAtIndex:buttonIndex];
}
// 点击后再让其调用dealloc
if ([sd_alertSheets containsObject:self]) {
[sd_alertSheets removeObject:self];
}
}
- (void)dealloc {
DLog(@"SDAlertView dealloc");
}
@end
不定参数获取
在初始化方法initWithTitle中,我们需要获取不定参数,这里通过va_list指针,获取参数代码如下
// 后面的不定参数,可以使用va_list来获取
//va_list 是一个指针变量 本质上是一个指针
va_list arg_lists;
//初始化 va_list
va_start(arg_lists, otherButtonTitles);
NSString * obj = nil;
//va_arg(arg_lists, NSString*)用户来获取值
while((obj=va_arg(arg_lists, NSString*))){
if (obj && obj.length > 0) {
[tmpTitleList addObject:[NSString stringWithFormat:@"%@", obj]];
}
};
//结束参数获取
va_end(arg_lists);
静态sd_alertSheets和UIAlertAction扩展
sd_alertSheets是一个静态数组,使用sd_alertSheets是为了保证SDAlertView在代理事件处理后再释放SDAlertView对象,调用其dealloc方法。
// 采用这种方式,当点击的时候,将其从列表移除,防止在调用handleAlertAction时候self被释放掉了。
// 调用handleAlertAction,将其从数组中移除,移除后自动调用dealloc方法
UIAlertAction为增加属性,动态增加属性,一般采用runtime的方法。
#import <UIKit/UIKit.h>
static NSMutableArray *sd_alertSheets;
@interface UIAlertAction (SDIndex)
@property (nonatomic, assign) NSInteger actionIndex;
@end
#import "UIAlertAction+SDIndex.h"
static void *kActionIndexStrKey = &kActionIndexStrKey;
@implementation UIAlertAction (CSExt)
- (NSInteger)actionIndex {
NSNumber *actionIndxNum = objc_getAssociatedObject(self, kActionIndexStrKey);
return [actionIndxNum integerValue];
}
- (void)setActionIndex:(NSInteger)actionIndex {
objc_setAssociatedObject(self, kActionIndexStrKey, [NSNumber numberWithInteger:actionIndex], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
封装好SDAlertView类,替换工程UIAlertView
封装好SDAlertView类,替换工程UIAlertView,这时候,我们可以全局替换掉系统的UIAlertView,将UIAlertView替换成SDAlertView即可。
当然系统中的UIActionSheet替换方式一样,可以自定义类SDActionSheet采用同样的方式替换。
对于一些方法addButtonWithTitle、buttonTitleAtIndex、cancelButtonIndex您也可以尝试新增一些方法完善一下。
小结
iOS开发 在处理一些代码时候,自己常用的做法做一层封装,方便统一管理与替换。https://blog.csdn.net/gloryFlow/article/details/144538615