美文网首页
超级好用的可高度自定义的弹窗组件(CLAlert)

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

作者: YesWeCan | 来源:发表于2020-03-11 16:31 被阅读0次

    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<NSString *> *)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<NSString *> *)defaultBtns
                      clickAction:(CLClickBlock __nullable)clickBlock;
    
    ///Sheet:有多个默认样式按钮+一个取消
    +(void)SheetWithTitle:(NSString * __nullable)title
                          message:(NSString * __nullable)message
                 defaultBtnsArray:(NSArray<NSString *> *)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<CLBaseAlertProtocol>
    /**
     弹窗对象的唯一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<CLBaseAlertProtocol>
    /**
     弹窗对象的唯一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<NSString *> *)buttonsArray
                         priority:(CLAlertPriority)priority
                      clickAction:(CLClickBlock __nullable)clickBlock;
    
    ///Alert:有多个默认样式按钮的Alert弹窗
    +(instancetype)AlertWithTitle:(NSString * __nullable)title
                          message:(NSString * __nullable)message
                 defaultBtnsArray:(NSArray<NSString *> *)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<NSString *> *)defaultBtns
                      clickAction:(CLClickBlock __nullable)clickBlock;
    
    ///Sheet:有多个默认样式按钮+一个取消
    +(instancetype)SheetWithTitle:(NSString * __nullable)title
                          message:(NSString * __nullable)message
                 defaultBtnsArray:(NSArray<NSString *> *)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<CLBaseAlertProtocol>)alert;
    
    +(void)removeAlertByIdx:(NSString *)idx;
    
    +(void)clearAllAlerts;
    
    +(id<CLBaseAlertProtocol>)getAlertByIdx:(NSString *)idx;
    
    +(NSArray<NSString *> *)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<CLBaseAlertProtocol> _Nonnull);
    @protocol CLBaseAlertProtocol <NSObject>
    @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)

          本文链接:https://www.haomeiwen.com/subject/crdcjhtx.html