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. 【servlet】 第一个servlet

    简单打印haha Helloyt.java package day01; import java.io.IOException; import javax.servlet.ServletConfig; ...

  2. 【中国剩余定理】 poj 1006

    生理周期  简单模拟 对于超出23 * 28 * 33(21252)时进行求余运算即可. #include<stdio.h> int main() { //freopen("in ...

  3. HTML+CSS D08浮动

    1. <html> <head> <title>div浮动</title> <style type="text/css"> ...

  4. CSS样式 初学

    CSS样式 参考网站: CSS用法:3种 一:直接样式表 如<p style="color:red;">这是一个段落</p> 二:内部样式表 如:<s ...

  5. liunx 平台下软件包管理

    RPM/DPKG 两大阵营简介 在 GNU/Linux( 以下简称 Linux) 操作系统中,RPM 和 DPKG 为最常见的两类软件包管理工具,他们分别应用于基于 RPM 软件包的 Linux 发行 ...

  6. GameUnity 2.0 发布倒计时

    万众期待的 gameunity 网络游戏框架 已经完成了,现在在最后的检验调试阶段. 因为版本 改动非常之大,所以 版本号 从0.2版本 改成 2.0版本. gameunity事件部分,一如既往保持高 ...

  7. 手游 ui布局

    最近突然有做手游的冲动,其实也是酝酿好久了. 之前的demo 让我想做一款 策略类的 战争游戏,有点像 部落战争: 那么,让我最最头疼的就是 ui设计了. 国内大部分 游戏的 ui  都是 用各种各样 ...

  8. CenOS配置VSFTP服务器

    1 Linux FTP服务器分类: wu-ftp proftp=profession ftp vsftp=very security ftp 2 安装vsftp yum install vsftp 3 ...

  9. Java Web 错误排查

    排查404 1. 检查web.xml,有没有放在web-inf下面,再检查过滤器有没有配置 <filter> <filter-name>struts</filter-na ...

  10. js List<Map> 将偏平化的数组转为树状结构并排序

    数据格式: [ { "id":"d3e8a9d6-e4c6-4dd8-a94f-07733d3c1b59", "parentId":&quo ...