iOS开发 UIAlertView与UIActionSheet替换方案之SDAlertView与SDActionSheet

发布于:2024-12-18 ⋅ 阅读:(13) ⋅ 点赞:(0)

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