iOS 在显示另一个之前关闭 UIAlertView
Posted
技术标签:
【中文标题】iOS 在显示另一个之前关闭 UIAlertView【英文标题】:iOS dismiss UIAlertView beforing showing another 【发布时间】:2011-07-20 22:20:25 【问题描述】:我有一个 Utils 类,它会在触发某些通知时显示 UIAlertView。有没有办法在显示新的 UIAlertViews 之前关闭任何打开的 UIAlertViews?
当应用程序进入后台时,我正在这样做
[self checkViews:application.windows];
关于 applicationDidEnterBackground
- (void)checkViews:(NSArray *)subviews
Class AVClass = [UIAlertView class];
Class ASClass = [UIActionSheet class];
for (UIView * subview in subviews)
if ([subview isKindOfClass:AVClass])
[(UIAlertView *)subview dismissWithClickedButtonIndex:[(UIAlertView *)subview cancelButtonIndex] animated:NO];
else if ([subview isKindOfClass:ASClass])
[(UIActionSheet *)subview dismissWithClickedButtonIndex:[(UIActionSheet *)subview cancelButtonIndex] animated:NO];
else
[self checkViews:subview.subviews];
这使得 applicationDidEnterBackground 变得容易,因为我可以使用 application.windows
我可以使用 AppDelegate 或类似的东西来获取所有视图、循环它们并关闭任何 UIAlertViews 吗?
【问题讨论】:
【参考方案1】:for (UIWindow* window in [UIApplication sharedApplication].windows)
NSArray* subviews = window.subviews;
if ([subviews count] > 0)
if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
[(UIAlertView *)[subviews objectAtIndex:0] dismissWithClickedButtonIndex:[(UIAlertView *)[subviews objectAtIndex:0] cancelButtonIndex] animated:NO];
【讨论】:
……这就是为什么迭代私有内部视图结构是一个脆弱的过程。 对不起,我使用了相同的代码,但它不适合我。 这不再适用于 ios 7。 _UIModalItemHostingWindow中添加了Alertview,并没有对其的引用。 有谁知道如何在 ios 8 或更高版本中完成上述任务。【参考方案2】:iOS6兼容版本:
for (UIWindow* w in UIApplication.sharedApplication.windows)
for (NSObject* o in w.subviews)
if ([o isKindOfClass:UIAlertView.class])
[(UIAlertView*)o dismissWithClickedButtonIndex:[(UIAlertView*)o cancelButtonIndex] animated:YES];
【讨论】:
你找到 iOS 7 的替代品了吗?【参考方案3】:iOS7兼容版本:
我做了一个类接口,将所有实例存储在init方法中。
我知道这是一种非常低效的方法。
#import <objc/runtime.h>
#import <objc/message.h>
@interface UIAlertView(EnumView)
+ (void)startInstanceMonitor;
+ (void)stopInstanceMonitor;
+ (void)dismissAll;
@end
@implementation UIAlertView(EnumView)
static BOOL _isInstanceMonitorStarted = NO;
+ (NSMutableArray *)instances
static NSMutableArray *array = nil;
if (array == nil)
array = [NSMutableArray array];
return array;
- (void)_newInit
[[UIAlertView instances] addObject:[NSValue valueWithNonretainedObject:self]];
[self _oldInit];
- (void)_oldInit
// dummy method for storing original init IMP.
- (void)_newDealloc
[[UIAlertView instances] removeObject:[NSValue valueWithNonretainedObject:self]];
[self _oldDealloc];
- (void)_oldDealloc
// dummy method for storing original dealloc IMP.
static void replaceMethod(Class c, SEL old, SEL new)
Method newMethod = class_getInstanceMethod(c, new);
class_replaceMethod(c, old, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
+ (void)startInstanceMonitor
if (!_isInstanceMonitorStarted)
_isInstanceMonitorStarted = YES;
replaceMethod(UIAlertView.class, @selector(_oldInit), @selector(init));
replaceMethod(UIAlertView.class, @selector(init), @selector(_newInit));
replaceMethod(UIAlertView.class, @selector(_oldDealloc), NSSelectorFromString(@"dealloc"));
replaceMethod(UIAlertView.class, NSSelectorFromString(@"dealloc"), @selector(_newDealloc));
+ (void)stopInstanceMonitor
if (_isInstanceMonitorStarted)
_isInstanceMonitorStarted = NO;
replaceMethod(UIAlertView.class, @selector(init), @selector(_oldInit));
replaceMethod(UIAlertView.class, NSSelectorFromString(@"dealloc"), @selector(_oldDealloc));
+ (void)dismissAll
for (NSValue *value in [UIAlertView instances])
UIAlertView *view = [value nonretainedObjectValue];
if ([view isVisible])
[view dismissWithClickedButtonIndex:view.cancelButtonIndex animated:NO];
@end
在使用 UIAlertView 之前启动实例监控。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//...
//...
[UIAlertView startInstanceMonitor];
return YES;
在显示另一个之前调用dismissAll。
[UIAlertView dismissAll];
如果您可以控制所有 UIAlertView,最好使用单例模式。
但就我而言,我需要这段代码来关闭 UIWebView 中的 javascript 警报对话框。
【讨论】:
【参考方案4】:由于 UIAlertView
在 iOS8 中被弃用,取而代之的是 UIAlertController
(这是一个 UIViewController
,以模态方式呈现),您不能同时预设 2 个警报(至少来自同一个 viewController)。根本不会显示第二个警报。
我想部分模仿UIAlertView
的行为,并防止同时显示多个警报。 Bellow 是我的解决方案,它使用窗口的rootViewController
来呈现警报(通常是appDelegate 的导航控制器)。我在 AppDelegate 中声明了这一点,但你可以把它放在你想要的地方。
如果您在使用中遇到任何问题,请在 cmets 此处报告。
@interface UIViewController (UIAlertController)
// these are made class methods, just for shorter semantics. In reality, alertControllers
// will be presented by window's rootViewController (appdelegate.navigationController)
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
handler:(void (^)(NSInteger buttonIndex))block;
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle;
@end
@implementation UIViewController (UIAlertController)
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
return [self presentAlertWithTitle:title message:message cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil handler:nil];
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
handler:(void (^)(NSInteger buttonIndex))block
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction *action)
if (block)
block(0);
];
[alert addAction:cancelAction];
[otherButtonTitles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL *stop)
UIAlertAction *action = [UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault handler:^(UIAlertAction *action)
if (block)
block(idx + 1); // 0 is cancel
];
[alert addAction:action];
];
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
UIViewController *rootViewController = appDelegate.window.rootViewController;
if (rootViewController.presentedViewController)
[rootViewController dismissViewControllerAnimated:NO completion:^
[rootViewController presentViewController:alert animated:YES completion:nil];
];
else
[rootViewController presentViewController:alert animated:YES completion:nil];
return alert;
@end
【讨论】:
以上是关于iOS 在显示另一个之前关闭 UIAlertView的主要内容,如果未能解决你的问题,请参考以下文章
iOS 恢复 - 为啥在我正在恢复的视图之前显示另一个视图?
在所有内容之上显示一个 UIAlertController 并保持在顶部,即使推送另一个视图(iOS 13)