超级好用的可高度自定义的弹窗组件(CLAlert)

1 前言

弹窗组件是每个App必不可少的组成部分,部分对弹窗要求不是特别高的App可能会直接使用UIAlertController来解决。但是大部分产品经理是不会用系统默认的样式,他们总要在上面做出调整以显示App的精美。我在开发这个组件时,刚刚开始是想写一个简单的弹窗控件,就是将一个UIVIew贴到UIWindow上即可。但是随着对产品各种需求场景的理解,逐渐让我丰富了它的功能,最后提炼成一个完整的可以高度自定义的弹窗、提示、Toast等大全控件。
目前,我们开发的样式满足了我们产品的需求,所以没有去丰富它的其他样式,如果你需要扩展你们的样式,相信这篇文章可以帮到你。如果你是为了快速直接用,那么目前CLAlert提供的样式可能不能满足你。

2 介绍

1.先说说如何使用
快捷使用,可以查看CLAlertHelper类,主要分为两个模块:Apple系统级样式弹窗、自定义样式躺床。系统级就是对UIAlertContoller的封装,自定义就是基于UIView的一些封装。
系统样式的快捷方法如下:

#pragma mark- 系统弹窗样式
///Alert:有多个默认样式按钮的Alert弹窗
+(void)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Alert:只有一个默认样式按钮的Alert弹窗
+(void)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
                   defaultBtn:(NSString *)defaultTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;


///Alert:一个默认样式按钮+一个默认红色样式按钮
+(void)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
                   defaultBtn:(NSString *)defaultTxt
                detructiveBtn:(NSString *)destructiveTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:有多个默认样式按钮的Sheet弹窗
+(void)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:有多个默认样式按钮+一个取消
+(void)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                    cancelBtn:(NSString *)cancelTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:一个红色按钮+一个取消
+(void)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
               destructiveBtn:(NSString * __nullable)destructive
                    cancelBtn:(NSString *)cancelTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

自定义方式的方法如下:

#pragma mark- 自定义样式
///自定义样式使用
+(void)AlertMsg:(NSString *)msg
           WithCallback:(CLClickBlock)callback;

///自定义样式使用
+(void)AlertMsg2:(NSString *)msg
            WithCallback:(CLClickBlock)callback;

///自定义样式使用
+(void)AlertBottom:(NSString *)msg
            action:(NSString *)btnTxt
      WithCallback:(CLClickBlock)callback;

///自定义样式使用
+(void)AlertToast:(NSString *)msg
      WithCallback:(CLClickBlock)callback;

目前提供的效果如下:

图1 自定义样式.png
图2 自定义样式.png
图3 系统级样式.png

图4系统级sheet样式.png

图5系统级alert样式.png

图6 自定义Toast样式.png

图7自定义底部提示框样式.png
  1. 自定义样式
    如上面效果展示的图1、图2、图7都是自定义样式
    对于自定义的样式,你需要先自己确定一个UIVIew,然后参考CLCustomizeAlert中的实现:
    图一自定义样式参考代码:
+(instancetype)alertMsg:(NSString *)msg WithCallback:(CLClickBlock)callback{
    MMAlertView *alertView = [[MMAlertView alloc] initWithMessage:msg firstTitle:@"第一行标题" secondTitle:@"第二行标题" delegete:nil];
    CLCustomizeAlert * alertC = (CLCustomizeAlert *)[CLCustomizeAlert popupWithContentView:alertView];
    alertView.delegate = alertC;
    alertC.shouldDismissOnBackgroundTouch = NO;
    alertC.shouldDismissOnContentTouch = NO;
    alertC.callback = [callback copy];

    return alertC;
}

图二自定义样式参考代码:

+(instancetype)alertMsg2:(NSString *)msg WithCallback:(CLClickBlock)callback{
    MMAlertView *alertView = [[MMAlertView alloc] initWithADMessage:@"我是广告弹窗" firstTitle:@"可爱的弹窗" secondTitle:@"不可爱的弹窗" delegete:nil];
    CLCustomizeAlert * alertC = (CLCustomizeAlert *)[CLCustomizeAlert
                                   popupWithContentView:alertView
                                   showType:(CLPopupShowTypeSlideInFromBottom)
                                                dismissType:(CLPopupDismissTypeSlideOutToBottom)
                                                       maskType:(CLPopupMaskTypeDimmed)
                                       dismissOnBackgroundTouch:NO
                                          dismissOnContentTouch:NO];
    alertView.delegate = alertC;
    alertC.shouldDismissOnBackgroundTouch = NO;
    alertC.shouldDismissOnContentTouch = NO;
    alertC.callback = [callback copy];
    CGSize size = [UIScreen mainScreen].bounds.size;
    alertC.showPoint = [NSValue valueWithCGPoint:CGPointMake(size.width/2, size.height-alertView.frame.size.height/2-20)];
    
    return alertC;
}

图7的底部展示样式的参考代码:

+(instancetype)alertBottomMsg:(NSString *)msg
                    actionBtn:(NSString *)action
                 WithCallback:(CLClickBlock)callback{
    MMAlertView * alertView = [[MMAlertView alloc] initWithBottomMessage:msg actionBtn:action];
    CLCustomizeAlert * alertC = (CLCustomizeAlert *)[CLCustomizeAlert
    popupWithContentView:alertView
    showType:(CLPopupShowTypeSlideInFromBottom)
                 dismissType:(CLPopupDismissTypeSlideOutToBottom)
                        maskType:(CLPopupMaskTypeDimmed)
        dismissOnBackgroundTouch:NO
           dismissOnContentTouch:NO];
    alertView.delegate = alertC;
    alertC.maskType = CLPopupMaskTypeNone;
    alertC.shouldDismissOnBackgroundTouch = NO;
    alertC.shouldDismissOnContentTouch = NO;
    alertC.callback = [callback copy];
    CGSize size = [UIScreen mainScreen].bounds.size;
    alertC.showPoint = [NSValue valueWithCGPoint:CGPointMake(size.width/2, size.height-alertView.frame.size.height/2-20)];
    return alertC;
}

而对于系统级样式的使用比较简单,可以参考类CLSystemBaseAlert的封装。

3 实现

整个组件主要结构如下:

image.png

CLPopup 是所以自定义弹窗的基类,它封装了UIView展示、交互、动画以及各种布局。
CLBaseAlert是CLPopup子类,它封装了弹窗非UI相关的基础数据,同时它也实现了CLBaseAlertProtocol协议。 实现该协议,即可被加入CLAlertSchedule调度管理器,用于控制弹窗的展示优先级,从而不会出现弹窗重叠的现象:

/**
 弹窗基类: 非UI相关的基础数据
 */
@interface CLBaseAlert : CLPopup
/**
 弹窗对象的唯一id
 */
@property (nonatomic, strong) NSString * idx;
/**
 弹窗优先级
 */
@property (nonatomic, assign) CLAlertPriority priority;
@property (nonatomic, copy) CLCompletion completion;

+ (CLBaseAlert*)popupWithContentView:(UIView*)contentView;

+ (CLBaseAlert*)popupWithContentView:(UIView*)contentView
                        showType:(CLPopupShowType)showType
                     dismissType:(CLPopupDismissType)dismissType
                        maskType:(CLPopupMaskType)maskType
        dismissOnBackgroundTouch:(BOOL)shouldDismissOnBackgroundTouch
           dismissOnContentTouch:(BOOL)shouldDismissOnContentTouch;

- (void)show;

- (void)dismiss;
@end

CLCustomizeAlert是继承于CLBaseAlert的子类,它是负责封装UI相关的逻辑,你可以在上面添加自定义UI,例如MMAlertView的UI样式。
CLSystemBaseAlert是继承于UIAlertController的封装:

@interface CLSystemBaseAlert : UIAlertController
/**
 弹窗对象的唯一id
 */
@property (nonatomic, strong) NSString * idx;

/// 弹窗操作完成的回调
@property (nonatomic, copy) CLCompletion completion;
/**
 弹窗优先级
 */
@property (nonatomic, assign) CLAlertPriority priority;

/// 根便利构造器
/// @param title 标题
/// @param message 内容
/// @param preferredStyle 系统样式
/// @param buttonsArray 按钮数组
/// @param priority alert优先级
/// @param clickBlock 按钮点击操作
+(instancetype)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
               preferredStyle:(UIAlertControllerStyle)preferredStyle
                      buttons:(NSArray *)buttonsArray
                     priority:(CLAlertPriority)priority
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Alert:有多个默认样式按钮的Alert弹窗
+(instancetype)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Alert:只有一个默认样式按钮的Alert弹窗
+(instancetype)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
                   defaultBtn:(NSString *)defaultTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;


///Alert:一个默认样式按钮+一个默认红色样式按钮
+(instancetype)AlertWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
                   defaultBtn:(NSString *)defaultTxt
                detructiveBtn:(NSString *)destructiveTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:有多个默认样式按钮的Sheet弹窗
+(instancetype)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:有多个默认样式按钮+一个取消
+(instancetype)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
             defaultBtnsArray:(NSArray *)defaultBtns
                    cancelBtn:(NSString *)cancelTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

///Sheet:一个红色按钮+一个取消
+(instancetype)SheetWithTitle:(NSString * __nullable)title
                      message:(NSString * __nullable)message
               destructiveBtn:(NSString * __nullable)destructive
                    cancelBtn:(NSString *)cancelTxt
                  clickAction:(CLClickBlock __nullable)clickBlock;

-(void)show;

///iPad下使用
@property (nonatomic, assign) CGRect sourceRect;
@property (nullable, nonatomic, strong) UIView *sourceView;

@end

它也实现了CLBaseAlertProtocol协议,从而可以被加入调度管理器CLAlertScheduler,对展示的优先级进行设置。
CLAlertScheduler是管控所有弹窗的调度器,主要方法如下:

@interface CLAlertScheduler : NSObject

+(void)addAlertToScheduler:(id)alert;

+(void)removeAlertByIdx:(NSString *)idx;

+(void)clearAllAlerts;

+(id)getAlertByIdx:(NSString *)idx;

+(NSArray *)allAlertIds;

@end

CLBaseAlertProtocol 是所有弹窗需要遵守的调度协议:

typedef NS_ENUM(NSInteger, CLAlertPriority){
    CLAlertPriorityLow = -1,
    CLAlertPriorityDefault = 0,
    CLAlertPriorityHigh = 1
};
@protocol CLBaseAlertProtocol;

typedef void(^CLClickBlock)(NSString * _Nonnull title);
typedef void(^CLCompletion)(id _Nonnull);
@protocol CLBaseAlertProtocol 
@required
/**
 弹窗对象的唯一id
 */
@property (nonatomic, strong) NSString * idx;

/// 弹窗操作完成的回调(唤起下一个弹窗的作用,链式调用)
@property (nonatomic, copy) CLCompletion completion;
/**
 弹窗优先级
 */
@property (nonatomic, assign) CLAlertPriority priority;

-(void)show;

-(void)dismiss;
@end

它需要每个弹窗实例定义唯一的id,优先级,以及显示 和 展示的方法。如果我们后面在开发时,需要定义更高级的弹窗,可以直接修改CLAlertPriority的枚举类型来实现。
最后,是CLAlertHelper对我们通用的样式进行简单封装。
以上便是该弹窗的整个实现流程,你可以根据需要进行修改,以应对你的弹窗定制化需求。

你可能感兴趣的:(超级好用的可高度自定义的弹窗组件(CLAlert))