http://www.jianshu.com/p/7ac398ef4532

项目中可能会遇到这种情况,好几个alertView因为逻辑关系全部弹出,用户需要一个个的点击才能将所有的alertView取消掉。或者说这种情况下,我们只需要弹出一个alertView就OK了。

alertView是怎么弹出的?网上查找资料说是,每次执行[alertView show],这个方法的时候是新建了一个window,将alertView显示在了window上面。代码验证的确是这样的。

代码验证alertView是添加到哪里的。

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. UIButton *tempBtn = [UIButton buttonWithType:UIButtonTypeSystem];
tempBtn.frame = CGRectMake(100, 100, 100, 100);
tempBtn.backgroundColor = [UIColor cyanColor];
[tempBtn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:tempBtn]; } - (void)clickBtn:(UIButton *)sender
{
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"title1" message:@"message1" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert1 show]; NSLog(@"alert1.window = %@ alert1.window.windowLevel = %f",alert1.window,alert1.window.windowLevel);
NSLog(@"app.window = %@",app.window);
NSLog(@"windows == %@",[UIApplication sharedApplication].windows);
}

测试结果:

alert1.window = <_UIAlertControllerShimPresenterWindow: 0x7f9ee8c07940; frame = (0 0; 414 736); opaque = NO; gestureRecognizers = <NSArray: 0x618000056aa0>; layer = <UIWindowLayer: 0x6180000240a0>>   alert1.window.windowLevel = 2001.000000
app.window = <UIWindow: 0x7f9ee8f03f80; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x608000052f60>; layer = <UIWindowLayer: 0x608000022100>>
windows == (
"<UIWindow: 0x7f9ee8f03f80; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x608000052f60>; layer = <UIWindowLayer: 0x608000022100>>"
)

通过打印的结果可以看出:
1、alert1.window没有在[UIApplication sharedApplication].windows中出现window和windows的关系参考:http://www.jianshu.com/p/75befce85623,windows中只有app.window也就是当前的最底层的控件。
2、alert1.window的windowLevel是2001比app.window的大,APP.window的windowLevel是0,所以alertView显示在了app.window的上面。相关windowLevel的问题参考:http://www.jianshu.com/p/f60471a7d935

搞懂了alertView显示的大致原理了,那么往我们的需求上靠

- (void)clickBtn:(UIButton *)sender
{
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"title1" message:@"message1" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert1 show]; UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"title2" message:@"message2" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert2 show]; // AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
//
// NSLog(@"alert1.window = %@ alert1.window.windowLevel = %f",alert1.window,alert1.window.windowLevel);
// NSLog(@"app.window = %@",app.window);
// NSLog(@"windows == %@",[UIApplication sharedApplication].windows);
}

想要多余的alertView不显示,两种方法:
1、要么让他不展示
2、要么让他展示了自己再消失
第一种我感觉做不到,显示的逻辑是写死的。
那就拿第二种下手,前提是怎么获取到已经展示的alertView?

上面介绍的alertView显示,是显示在系统给自己创建的Window上面的,但是这个window还获取不到。那怎么办。
有这么一种思路,将所有显示的alertView记录在自己的一个数组中,然后不就想干嘛就干嘛了嘛!!关键点是记录的时机,这里选取show方法执行的时候
思路1:
使用runtime方法检测show方法,然后在执行show方法的时候记录alertView,相关代码如下:
创建记录alertView的单例

#import <Foundation/Foundation.h>

@interface AlertViewRecorder : NSObject

@property (nonatomic, strong)NSMutableArray * alertViewArray;

+ (AlertViewRecorder *)shareAlertViewRecorder;

@end
#import "AlertViewRecorder.h"

@implementation AlertViewRecorder
// 创建单例,记录alertView
+ (AlertViewRecorder *)shareAlertViewRecorder
{
static AlertViewRecorder *recoder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if(recoder == nil){
recoder = [[AlertViewRecorder alloc] init]; }
});
return recoder;
} - (instancetype)init
{
self = [super init];
if (self) {
self.alertViewArray = [[NSMutableArray alloc] init];
}
return self;
} @end

关键代码

#import "UIAlertView+MyAlertView.h"
#import <objc/message.h>
#import "AppDelegate.h"
#import "AlertViewRecorder.h" @implementation UIAlertView (MyAlertView) + (void)load
{
// 获取将要交换的两个方法
Method showMethod = class_getInstanceMethod(self, @selector(show));
Method myShowMethod = class_getInstanceMethod(self, @selector(myShow));
// 将两个方法互换
method_exchangeImplementations(showMethod, myShowMethod); } - (void)myShow
{
// 将之前所有的alertView取出来消失掉
NSMutableArray *array = [AlertViewRecorder shareAlertViewRecorder].alertViewArray;
for (UIAlertView *alertView in array) {
if ([alertView isKindOfClass:[UIAlertView class]]) {
[alertView dismissWithClickedButtonIndex:-1 animated:YES];
}
} [array removeAllObjects];
// 调用自身的方法
[self myShow];
[array addObject:self];
} @end

测试代码可行;

思路2:
创建分类,重写show方法,在重写的show方法中调用show方法的同时,记录alertView到相关数组,和思路1差不多。

思路1相对于思路2的优点,个人认为,当项目开发了一段时间或者半路接手项目的时候,思路1更有优势。

如有失误请各位路过大神即时指点,或有更好的做法,也请指点一二,在下感激不尽。
代码连接:https://github.com/RunOfTheSnail/MyAlertViewDemo

文/小岩同学(简书作者)
原文链接:http://www.jianshu.com/p/7ac398ef4532
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

防止多个UIAlertView重叠弹出的更多相关文章

  1. 自定义一个类似UIAlertView的弹出框

    这个是和UIAlertView类似,但是可以自定义view的样式废话不多说,上代码: 首先第一步:创建一个继承自View的类如: #import <UIKit/UIKit.h> @clas ...

  2. UIAlertView弹出视图动画效果

    在App设计中为了加强用户体验,我们会常常加入一些友好的动画效果.比如类似UIAlertView弹出的动画效果,由于系统中并没有直接提供类似的动画API,如果我们想要做出一样的效果,那就得深入的研究一 ...

  3. JQuery EasyUI dialog弹出框的 close 和 destroy

    开发项目中(使用JQuery EasyUI),根据业务需要重叠弹出多个提示框的情况,会出现如下情况:页面出现两个div模块调用同一个弹出页面,页面的数据接受框元素不能实时存储数据解决方案: 使用$(t ...

  4. UIAlertView弹出框

    <Alert弹出框提示用户信息>    1.遵循代理方法<UIAlertViewDelete>    2.调用方法UIAlertView *alert = [[UIAlertV ...

  5. 在没有界面的类中,实现弹出UIAlertView || 在没有界面的类中,刷新程序界面 思路

    +(DisplayErrorMsg *)sharedDisplayErrorMsg { static DisplayErrorMsg *instance = nil; @synchronized(in ...

  6. iOS自定义提示弹出框(类似UIAlertView)

    菜鸟一枚,大神勿喷.自己在牛刀小试的时候,发现系统的UIAlertView有点不喜欢,然后就自己自定义了一个UIAlertView,基本上实现了系统的UIAlertView,可以根据项目的需求修改UI ...

  7. 使Toast弹出不重叠的封装

    一.问题 在频繁弹出toast的时候,弹出后出现延迟重叠的现象. 二.解决 Toast通常由makeTextT()方法实例化,如何不想要toast弹出时重叠,那么只需在应用中保持一个Toast对象即可 ...

  8. 阶段一:为View设置阴影和弹出动画(天气应用)

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 上一篇阶段一:通过网络请求,获得并解析JSON数据(天气应用)完成了应用的核心功能,接下来就要对它进行优化.今天我 ...

  9. 【代码笔记】iOS-轮询弹出框

    一,效果图. 二,工程图. 三,代码. RootViewController.m #import "RootViewController.h" //加入弹出框的头文件 #impor ...

随机推荐

  1. C++虚函数的新用法

    1.今天在segmentfault上看到了一个C++虚函数的新用法,先上代码 #include <iostream> using namespace std; class B { publ ...

  2. loadrunner基本概念、安装及术语(一)

    一.初识loadrunner: LoadRunner,是一种预测系统行为和性能的负载测试工具.通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner能够对整个企业架 ...

  3. python 压缩 解压缩 文件

    1. 用zipfile模块打包文件或是目录.解压zip文件 http://wangwei007.blog.51cto.com/68019/1045577 #!/usr/bin/env python # ...

  4. display:block;inline;inline-block大总结

    总体概念 block和inline这两个概念是简略的说法,完整确切的说应该是 block-level elements (块级元素) 和 inline elements (内联元素).block元素通 ...

  5. hdu 3342 Legal or Not(拓扑排序)

    Legal or Not Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total ...

  6. java 强弱引用

    ava中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了 ...

  7. hdu_4897_Little Devil I(树链剖分)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4897 题意:有三种操作,1是在树上的两个节点之间的路径改变当前的颜色,2是改变树上有且只有一个端点在u ...

  8. 使用print2flash开发在线文档

    www.print2flash.com 命令行调用: A:\Program Files (x86)\Print2Flash3>p2fServer.exe a.pdf a.swf

  9. php源码分析之PHPAPI宏的作用

    在PHP源码中,我们经常会看到很多函数前面有个PHPAPI,但这是什么呢? 于是我在php源码/main/php.h中找到了它的定义 #ifdef PHP_WIN32 # include " ...

  10. ural1037 Memory Management

    Memory Management Time limit: 2.0 secondMemory limit: 64 MB Background Don't you know that at school ...