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

你可能感兴趣的:(iOS多个弹框按顺序依次弹出)