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_listva_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];// 所有按钮titleNSMutableArray *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.titlemessage:self.messagepreferredStyle: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.cancelButtonTitlestyle:UIAlertActionStyleCancelhandler:^(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:titlestyle:UIAlertActionStyleDefaulthandler:^(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];}// 点击后再让其调用deallocif ([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_listva_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