問(wèn)題和需求
APP在啟動(dòng)完成后瞳遍,一般會(huì)在首頁(yè)有多個(gè)彈窗,這些彈窗大多都是自定義的,網(wǎng)絡(luò)回來(lái)的時(shí)機(jī)也不一定渗常。因此多個(gè)彈窗如果不做處理就會(huì)一起全部彈出來(lái)壮不,這樣會(huì)比較影響體驗(yàn),如果沒(méi)有好的管理方法凳谦,那么維護(hù)就變得困難忆畅。
先附上效果對(duì)比圖
這個(gè)效果是通過(guò)信號(hào)量實(shí)現(xiàn)的
信號(hào)量概念
信號(hào)量是用于多線程同步的,跟鎖不一樣的是尸执,信號(hào)量不一定是鎖定某一個(gè)資源家凯,而是流程上的概念,比如:有A,B兩個(gè)線程如失,B線程要等A線程完成某一任務(wù)以后再進(jìn)行自己下面的步驟绊诲,這個(gè)任務(wù) 并不一定是鎖定某一資源,還可以是進(jìn)行一些計(jì)算或者數(shù)據(jù)處理之類(lèi)褪贵。而對(duì)于鎖來(lái)說(shuō)掂之,鎖住的資源無(wú)法被其余的線程訪問(wèn),從而阻塞線程而實(shí)現(xiàn)線程同步脆丁。
實(shí)現(xiàn)
首先創(chuàng)建一個(gè)類(lèi)AlertViewManager世舰,實(shí)現(xiàn)單例,用于管理項(xiàng)目中的所有的彈窗槽卫,可以是自定義的彈窗跟压、原生彈窗、權(quán)限申請(qǐng)的彈窗
代碼建議拉到末尾下載DEMO歼培,粘貼代碼到簡(jiǎn)書(shū)有些空格不見(jiàn)了
AlertViewManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedefvoid(^ExecuteBlock)(void);
@interfaceAlertViewManager :NSObject
//重要提醒震蒋!:自定義的彈窗不要添加再[UIApplication sharedApplication].keyWindow上, keyWindow是會(huì)變化的,因?yàn)橄到y(tǒng)的彈窗是創(chuàng)建一個(gè)window躲庄,成為keyWindow查剖,當(dāng)系統(tǒng)彈窗消失的時(shí)候,創(chuàng)建出來(lái)的window也會(huì)銷(xiāo)毀噪窘,但是這時(shí)候獲取的keyWindow有可能就是準(zhǔn)備銷(xiāo)毀這個(gè).這樣的自定義彈窗連同keywindow一起被銷(xiāo)毀笋庄,導(dǎo)致信號(hào)一直是鎖著的狀態(tài),無(wú)法彈出后面的彈窗
//系統(tǒng)彈窗消失瞬間獲取到的keyWindow就是系統(tǒng)彈窗那個(gè)keyWindow倔监,自定義彈窗添加上去會(huì)隨著keywindow銷(xiāo)毀而銷(xiāo)毀
+ (nullable instancetype)shareManager;
/**
彈窗顯示的代碼寫(xiě)進(jìn)BLOCK塊內(nèi)
@paramexecuteBlock <#executeBlock description#>
*/
- (void)showWithExecuteBlock:(nonnull ExecuteBlock)executeBlock;
/**
彈窗顯示完畢調(diào)用
@paramexecuteBlock 可以為空但是一定要調(diào)用
*/
- (void)dismissWithExecuteBlock:(nullable ExecuteBlock)executeBlock;
@end
AlertViewManager.m
#import "AlertViewManager.h"
//全局信號(hào)量
dispatch_semaphore_t_globalInstancesLockSEM;
//執(zhí)行QUEUE的Name
char*QUEUE_NAME_SEM ="com.alert.queueSEM";
//初始化 -- 借鑒YYWebImage的寫(xiě)法
staticvoid_AlertViewInitGlobalSEM() {
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
_globalInstancesLockSEM = dispatch_semaphore_create(1);
});
}
@interface AlertViewManager ()
@end
@implementationAlertViewManager
- (void)showWithExecuteBlock:(ExecuteBlock)executeBlock {
dispatch_async(dispatch_queue_create(QUEUE_NAME_SEM, DISPATCH_QUEUE_SERIAL), ^{
dispatch_semaphore_wait(_globalInstancesLockSEM, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
if(executeBlock) {
executeBlock();
}
});
});
}
- (void)dismissWithExecuteBlock:(ExecuteBlock)executeBlock {
dispatch_async(dispatch_queue_create(QUEUE_NAME_SEM, DISPATCH_QUEUE_SERIAL), ^{
dispatch_semaphore_signal(_globalInstancesLockSEM);
dispatch_async(dispatch_get_main_queue(), ^{
if(executeBlock) {
executeBlock();
}
});
});
}
+ (instancetype)shareManager {
return[[self alloc]init];
}
+ (instancetype)allocWithZone:(struct_NSZone*)zone {
staticidinstan =nil;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
instan = [superallocWithZone:zone];
});
returninstan;
}
- (instancetype)init {
if(self= [superinit]) {
_AlertViewInitGlobalSEM();
}
return self;
}
@end
使用方法
系統(tǒng)原生彈窗調(diào)用如下
[[AlertViewManager shareManager] showWithExecuteBlock:^{
[[[UIAlertView alloc] initWithTitle:@"" message:@"彈窗1" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil] show];
}];
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[[AlertViewManager shareManager] dismissWithExecuteBlock:^{
}];
}
重要提醒直砂!:
1.自定義的彈窗不要添加再[UIApplication sharedApplication].keyWindow上, keyWindow是會(huì)變化的,因?yàn)橄到y(tǒng)的彈窗是創(chuàng)建一個(gè)window丐枉,成為keyWindow少漆,當(dāng)系統(tǒng)彈窗消失的時(shí)候像啼,創(chuàng)建出來(lái)的window也會(huì)銷(xiāo)毀辟狈,但是這時(shí)候獲取的keyWindow有可能就是準(zhǔn)備銷(xiāo)毀這個(gè).這樣的自定義彈窗連同keywindow一起被銷(xiāo)毀,導(dǎo)致信號(hào)一直是鎖著的狀態(tài)敦腔,無(wú)法彈出后面的彈窗
2.showWithExecuteBlock和dismissWithExecuteBlock要成對(duì)出現(xiàn)。如果沒(méi)有成對(duì)出現(xiàn)翎卓,信號(hào)量計(jì)數(shù)錯(cuò)誤纵潦,會(huì)同時(shí)彈出多個(gè)彈窗或者不彈出
附上Demo: https://github.com/1498522607/HZSAlertViewManager
有什么問(wèn)題歡迎評(píng)論留言