希望弹窗顺序弹出,但是又关心弹出的优先级。下面是自己简单的实现,不足之处,请指出。
.h
/**
⚠️⚠️⚠️:PRPopupManager
此管理类不关心弹窗的show和hide
只约束弹窗是否被拦截一个一个展示、或者某几个同时展示
*/
#import <Foundation/Foundation.h>
///优先级枚举
typedef NS_ENUM(NSUInteger, PRPopupPriority) {
PRPopupPriorityLow = 1, /// 低
PRPopupPriorityMedium, /// 中
PRPopupPriorityHigh /// 高
};
/// 回调
typedef void (^PRPopupBlock)(void);
// MARK: - PRPopupConfig 弹出内容配置信息
@interface PRPopupConfig : NSObject
/// 是否被拦截:默认YES
@property (nonatomic, assign) BOOL isIntercept;
/// 当前弹窗是否在展示
@property (nonatomic, assign) BOOL isShowing;
/// 弹窗优先级:默认为High
@property (nonatomic, assign) PRPopupPriority priority;
/// 弹窗标识:以类名为标识,便于排查
@property (nonatomic, copy, nonnull) NSString *popupClassName;
/// 展示回调
@property (nonatomic, copy, nonnull) PRPopupBlock showBlock;
/// 隐藏回调
@property (nonatomic, copy, nullable) PRPopupBlock dismissBlock;
@end
// MARK: - PRPopupManager 弹出内容管理类
@interface PRPopupManager : NSObject
/// 单例对象
+ (instancetype _Nonnull)shared;
/// 展示弹窗
/// @param popupClass 弹出内容class
/// @param showBlock 展示回调
/// @param dismissBlock 隐藏回调
- (void)popupWithClass:(_Nonnull Class)popupClass show:(_Nonnull PRPopupBlock)showBlock dismiss:(_Nullable PRPopupBlock)dismissBlock;
/// 展示弹窗
/// @param popupClass 弹出内容class
/// @param priority 优先级
/// @param showBlock 展示回调
/// @param dismissBlock 隐藏回调
- (void)popupWithClass:(_Nonnull Class)popupClass priority:(PRPopupPriority)priority show:(_Nonnull PRPopupBlock)showBlock dismiss:(_Nullable PRPopupBlock)dismissBlock;
/// 展示弹窗
/// @param popupClass 弹出内容class
/// @param priority 优先级
/// @param isIntercept 是否需要被拦截
/// @param showBlock 展示回调
/// @param dismissBlock 隐藏回调
- (void)popupWithClass:(_Nonnull Class)popupClass priority:(PRPopupPriority)priority isIntercept:(BOOL)isIntercept show:(_Nonnull PRPopupBlock)showBlock dismiss:(_Nullable PRPopupBlock)dismissBlock;
/// 隐藏弹窗
- (void)dismissPopup;
@end
.m
#import "PRPopupManager.h"
// MARK: - PRAlertViewConfig
@implementation PRPopupConfig
- (instancetype)init {
self = [super init];
if (self) {
self.isIntercept = YES;
self.popupClassName = @"defaultName";
self.priority = PRPopupPriorityHigh;
}
return self;
}
@end
// MARK: - PRPopupManager
@interface PRPopupManager()
/// 弹出池:key:弹窗className、value:config对象
@property (nonatomic, strong) NSMutableDictionary *popupPool;
/// 当前弹窗内容
@property (nonatomic, strong) PRPopupConfig *currnetPopup;
@end
@implementation PRPopupManager
static PRPopupManager *_instance = nil;
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[PRPopupManager alloc] init];
});
return _instance;
}
// MARK: - Public Methods
- (void)popupWithClass:(Class)popupClass show:(PRPopupBlock)showBlock dismiss:(PRPopupBlock)dismissBlock{
[self popupWithClass:popupClass priority:PRPopupPriorityHigh isIntercept:YES show:showBlock dismiss:dismissBlock];
}
- (void)popupWithClass:(Class)popupClass priority:(PRPopupPriority)priority show:(PRPopupBlock)showBlock dismiss:(PRPopupBlock)dismissBlock {
[self popupWithClass:popupClass priority:priority isIntercept:YES show:showBlock dismiss:dismissBlock];
}
- (void)popupWithClass:(Class)popupClass priority:(PRPopupPriority)priority isIntercept: (BOOL)isIntercept show:(PRPopupBlock)showBlock dismiss:(PRPopupBlock)dismissBlock {
PRPopupConfig *config = [[PRPopupConfig alloc] init];
config.popupClassName = NSStringFromClass(popupClass);
config.priority = priority;
config.isIntercept = isIntercept;
[self popupWithConfig:config show:showBlock dismiss:dismissBlock];
}
// MARK: - Private Methods
- (void)popupWithConfig:(PRPopupConfig *)config show:(PRPopupBlock)showBlock dismiss:(PRPopupBlock)dismissBlock {
config.showBlock = showBlock;
config.dismissBlock = dismissBlock;
config.isShowing = YES;
// 加入弹出池,同一个弹窗避免重复添加
[self.popupPool setObject:config forKey:config.popupClassName];
// 当前有弹窗在显示:self.popupPool.allKeys.count > 1
// 当前弹窗被拦截:isIntercept == YES
if (config.isIntercept && self.popupPool.allKeys.count > 1) {
config.isShowing = NO;
return;
}
self.currnetPopup = config;
showBlock ? showBlock() : nil;
}
/// 清除弹出内容
- (void)dismissPopup {
// 获取到当前弹出内容并删除
[self deletePopupWithConfig:self.currnetPopup];
// 是否有弹窗在显示 是:拦截其他未显示弹窗;否:查看是否有下一个可显示弹窗并显示
if ([self isShowingSomeAlert]) {
return;
} else {
// 优先级排序
NSArray * values = [self.popupPool allValues];
values = [self sortByPriority:values];
// 当前没有正在展示的弹窗,则展示被拦截的弹窗
if (values.count > 0) {
// 查询是否有可以展示的弹窗:条件:1.已加入缓存、2.被拦截 3、实现了展示回调
// 优先级 1 > 2 > 3
// 找到一个立即展示,并退出循环
for (PRPopupConfig *config in values) {
PRPopupBlock showBlock = config.showBlock;
if (config.isIntercept && showBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
config.isShowing = YES;
self.currnetPopup = config;
showBlock();
});
break;
}
}
}
}
}
/// 是否正在展示某个弹窗
- (BOOL)isShowingSomeAlert{
__block BOOL isShowSomeAlert = NO;
[self.popupPool enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
PRPopupConfig *config = obj;
if (config.isShowing) {
isShowSomeAlert = YES;
*stop = YES;
}
}];
return isShowSomeAlert;
}
/// 根据优先级排序
/// @param configArray 弹窗配置数组
- (NSArray *)sortByPriority:(NSArray *)configArray {
NSComparator comparator = ^(PRPopupConfig *obj1, PRPopupConfig *obj2) {
if (obj1.priority > obj2.priority) {
return NSOrderedAscending;
}
if (obj1.priority < obj2.priority) {
return NSOrderedDescending;
}
return NSOrderedSame;
};
return [configArray sortedArrayUsingComparator:comparator];
}
/// 根据配置删除指定弹出内容
/// @param config 弹出配置
- (void)deletePopupWithConfig:(PRPopupConfig *)config {
if (config == nil) return;
PRPopupBlock dismissBlock = config.dismissBlock;
dismissBlock ? dismissBlock() : nil;
if ([self.popupPool.allKeys containsObject:config.popupClassName]) {
[self.popupPool removeObjectForKey:config.popupClassName];
}
self.currnetPopup = nil;
}
// MARK: - Getters
- (NSMutableDictionary *)popupPool {
if (!_popupPool) {
_popupPool = [[NSMutableDictionary alloc] init];
}
return _popupPool;
}
@end
调用:包裹一层,但是需要在弹窗消失的时候调用dismissPopup方法,将弹窗从数据源中移除。
[[PRPopupManager shared] popupWithClass:[PRPopPayerViewController class] priority:PRPopupPriorityLow show:^{
// 弹窗的展示
} dismiss:nil];
// 弹窗消失
[[PRPopupManager shared] dismissPopup];