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、图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 实现
整个组件主要结构如下:
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对我们通用的样式进行简单封装。
以上便是该弹窗的整个实现流程,你可以根据需要进行修改,以应对你的弹窗定制化需求。