问题
I have a Utils class which shows UIAlertView when certain notifications are triggered. Is there a way to dismiss any open UIAlertViews before showing a new one?
Currenty I am doing this when the app enters the background using
[self checkViews:application.windows];
on 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];
}
}
}
This makes it easy on applicationDidEnterBackground as I can use application.windows
Can I use the AppDelegate or anything similar to get all the views, loop through them and dismiss any 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];
}
回答2:
iOS6 compatible version:
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];
回答3:
iOS7 compatible version:
I made a category interface that stores all instance in init method.
I know it's a very inefficient way.
#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
Start instance monitoring before using UIAlertView.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//...
//...
[UIAlertView startInstanceMonitor];
return YES;
}
Call dismissAll before showing another.
[UIAlertView dismissAll];
It's better using a singleton pattern if you can control all UIAlertViews.
But in my case, I need this code for closing javascript alert dialog in a UIWebView.
回答4:
Since UIAlertView
is deprecated in iOS8 in favor of UIAlertController
(which is a UIViewController
, presented modally), you can't preset 2 alerts at the same time (from the same viewController at least). The second alert will simply not be presented.
I wanted to partially emulate UIAlertView
's behavior, as well as prevent showing multiple alerts at once. Bellow is my solution, which uses window's rootViewController
for presenting alerts (usually, that is appDelegate's navigation controller). I declared this in AppDelegate, but you can put it where you desire.
If you encounter any sorts of problems using it, please report here in comments.
@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
来源:https://stackoverflow.com/questions/5399219/ios-dismiss-uialertview-beforing-showing-another