iOS多个弹框按顺序依次弹出

前言

现在APP在首页会有多个弹框按照顺序依次弹出的功能,比如强制更新、比如弹出消息推送的活动弹框、比如弹出搜索弹框之类的,一般情况下自定义的 Alert 弹框,你把它在为 keyWindow 的中作为subview添加,这些弹框如果触发过程中同时触发,或者在其中一个弹框已经触发显示时,又触发了其他一个或者多个弹框,这时就会出现弹框叠加的一个效果。如果你用了毛玻璃背景,效果会更加明显,肯定不合适了。

所以,为了优化这个效果,我们需要当点击了第一个弹框的某个按钮之后,再弹出第二个弹框,以此类推。

这个就是我们实现的效果
未命名.2019-08-14 20_05_21.gif

参考的demo

具体实现我们参考了这个demo
这个demo想到用信号量去解决,但是信号量会阻塞线程,不可以直接在主线程使用,所以需要在子线程控制信号量,在主线程创建和显示 Alert。
代码如下:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    //创建一个队列,串行并行都可以,主要为了操作信号量
    dispatch_queue_t queue = dispatch_queue_create("com.se7en.alert", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        //创建一个初始为0的信号量
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        //第一个弹框,UI的创建和显示,要在主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"弹框1" message:@"第一个弹框" preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                //点击Alert上的按钮,我们发送一次信号。
                dispatch_semaphore_signal(sema);
            }]];
            [self presentViewController:alert animated:YES completion:nil];
        });

        //等待信号触发,注意,这里是在我们创建的队列中等待
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        //上面的等待到信号触发之后,再创建第二个Alert
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"弹框2" message:@"第二个弹框" preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                dispatch_semaphore_signal(sema);
            }]];
            [self presentViewController:alert animated:YES completion:nil];
        });

        //同理,创建第三个Alert
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"弹框3" message:@"第三个弹框" preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                dispatch_semaphore_signal(sema);
            }]];
            [self presentViewController:alert animated:YES completion:nil];
        });
    });
}

但是我们实际的操作过程比这个要复杂一些

第一点,我们的弹框是通过事件触发的,而且不是一次性触发的
第二点,可能在触发的同时又的弹框是不触发的,而且在同一时间可能触发多次

所以这个demo 还不能完全拿到我们的项目中去使用,我们需要在实际的代码中继续优化。

项目实践

优化一:子线程唯一

为什么需要保证线程唯一呢?因为我们的这个弹框触发时机、是否同时触发多个弹框、以及在其中一个弹框弹出的同时是否还有其他弹框触发,我们都是不可知的,如果线程不能保证唯一,这样就无法用信号量加上的锁有效,所以,我们需要创建一个唯一的队列,并且是串行队列。

// 创建对象
@property (nonatomic,assign) dispatch_queue_t myQueue;
//重写get方法确保线程唯一
- (dispatch_queue_t)myQueue{
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("alertViewDemo", DISPATCH_QUEUE_SERIAL);
    });
    return queue;
}

优化二:将所有弹框封装在一个方法中

将所有的弹框方法按照顺序添加到一个总的方法中,并且给出一个总的完成回掉。

- (void)showSerialQueueAlertViewCompletion:(void(^)(BOOL finished))completion{
    self.currentLoop++;
    NSString *loopStr = [NSString stringWithFormat:@"当前第%ld轮弹框",self.currentLoop];
    self.navigationItem.title = [NSString stringWithFormat:@"点击次数%ld",self.currentLoop];
    NSLog(@"开始执行  弹框 :%@",[NSThread currentThread]);
    //异步  这里会开启新的线程
    //为保证 在同一个线程 必须确保queue 是同一个同步队列
    dispatch_async(self.myQueue, ^{
        //创建一个初始为0的信号量
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        NSLog(@"串行队列执行 第一个 弹框 :%@",[NSThread currentThread]);
        //创建第1个Alert UI的创建和显示,要在主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"串行队列 主线程 执行 第一个 弹框 :%@",[NSThread currentThread]);
            if ([self showAlertView]) {
                MyAdvertisementView *view2 = [[MyAdvertisementView alloc] init];
                view2.title = [NSString stringWithFormat:@"%@,第一个",loopStr];
                MyBaseAlertView *alertView2 = [[MyBaseAlertView alloc] init];
                alertView2.animationType = MyBaseAlertViewAnimationTypeMoveFromBottomToBottom;
                [alertView2 showContentView:view2];
                [self.view addSubview:alertView2];
                view2.actionBlock = ^(UIButton *sender) {
                    [alertView2 dissmissContentView];
                    //点击Alert上的按钮,我们发送一次信号。
                    dispatch_semaphore_signal(sema);
                };
            }else{
                // 如果无需显示 直接发送信号量
                dispatch_semaphore_signal(sema);
            }
            
        });
        
        //等待信号触发,注意,这里是在我们创建的队列中等待
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        NSLog(@"串行队列执行 第二个 弹框 :%@",[NSThread currentThread]);
        //上面的等待到信号触发之后,再创建第二个Alert
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"串行队列 主线程 执行 第二个 弹框 :%@",[NSThread currentThread]);
            
            if ([self showAlertView]) {
                MyAdvertisementView *view2 = [[MyAdvertisementView alloc] init];
                view2.title = loopStr;
                MyBaseAlertView *alertView2 = [[MyBaseAlertView alloc] init];
                alertView2.animationType = MyBaseAlertViewAnimationTypeMoveFromTopToTop;
                [alertView2 showContentView:view2];
                [self.view addSubview:alertView2];
                view2.actionBlock = ^(UIButton *sender) {
                    [alertView2 dissmissContentView];
                    dispatch_semaphore_signal(sema);
                };
            }else{
                dispatch_semaphore_signal(sema);
            }
            
        });
       
        //同理,创建第N个Alert
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self showAlertView]) {
                
                MyAdvertisementView *view2 = [[MyAdvertisementView alloc] init];
                MyBaseAlertView *alertView2 = [[MyBaseAlertView alloc] init];
                view2.title = loopStr;

                alertView2.animationType = MyBaseAlertViewAnimationTypeMoveFromBottomToTop;
                [alertView2 showContentView:view2];
                [self.view addSubview:alertView2];
                view2.actionBlock = ^(UIButton *sender) {
                    [alertView2 dissmissContentView];
                    dispatch_semaphore_signal(sema);
                };
            }else{
                dispatch_semaphore_signal(sema);
            }
            
        });
        
        //同理,创建第N个Alert
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self showAlertView]) {
                MySuperSearchAlertView *view3 = [[MySuperSearchAlertView alloc] init];
                view3.title = [NSString stringWithFormat:@"%@,最后一个",loopStr];
                MyBaseAlertView *alertView3 = [[MyBaseAlertView alloc] init];
                [alertView3 showContentView:view3];
                [self.view addSubview:alertView3];
                view3.actionBlock = ^(UIButton *sender) {
                    [alertView3 dissmissContentView];
                    dispatch_semaphore_signal(sema);
                };
            }else{
                dispatch_semaphore_signal(sema);
            }
        });
        
        // 最终执行完毕 执行完毕回掉 同时也是为了衔接最后一个alert 和 下一轮的第一个alert 不然会出现同时弹出两个alert 的情况
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        NSLog(@"执行finished block 回掉");
        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_semaphore_signal(sema);
            if (completion) {
                completion(YES);
            }
        });
    });
    NSLog(@"添加完所有提示框");
}

这个是我的demo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 什么是推荐? 一言以蔽之,推荐是特定场景下人和信息更有效率都连接。 走进内容推荐 短期的规则干预是应该被长期的机制...
    91be18cb4a9f阅读 395评论 0 0
  • 20180717 入住简书第一天 晚上下班和老公的朋友们一起吃晚饭,刚刚迈进家门,还惦记着今天的1000字打卡...
    王小豆_7c09阅读 181评论 1 0