特定の通知がトリガーされたときにUIAlertViewを表示するUtilsクラスがあります。新しいUIAlertViewを表示する前に、開いているUIAlertViewを閉じる方法はありますか?
現在、アプリがを使用してバックグラウンドに入るときにこれを行っています
[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];
}
}
}
これにより、application.windowsを使用できるため、applicationDidEnterBackgroundで簡単に実行できます。
AppDelegateまたは同様のものを使用して、すべてのビューを取得し、それらをループしてUIAlertViewを閉じることはできますか?
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];
}
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];
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アラートダイアログを閉じるためにこのコードが必要です。
UIAlertView
はiOS8で廃止され、UIAlertController
(モーダルで表示されるUIViewController
)が優先されるため、2つのアラートを同時に(同じものから)プリセットすることはできません。少なくともviewController)。 2番目のアラートは単に表示されません。
UIAlertView
の動作を部分的にエミュレートし、一度に複数のアラートが表示されないようにしたかったのです。ベローは私のソリューションであり、アラートを表示するためにウィンドウのrootViewController
を使用します(通常、それはappDelegateのナビゲーションコントローラーです)。これはAppDelegateで宣言しましたが、好きな場所に配置できます。
それを使用して何らかの問題が発生した場合は、コメントでここに報告してください。
@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