在iOS 7中无法关闭UIAlertView?

7
我正在尝试在显示另一个警告框之前解除当前的警告框,我在这里找到了答案:iOS dismiss UIAlertView beforing showing another 问题是,在iOS7上无法正常工作,但在iOS6上可以。
这在iOS6上有效。
-(void)closePreviousAlert{
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];
}

有没有其他解决方案?

2
或者您可以使用单例封装的UIAlertView... - kgu87
6个回答

4

由于iOS7中[UIApplication sharedApplication].windows没有与UIAlertView相关的引用,因此您的代码在iOS7中无效,因为UIAlertView本身从未添加到任何窗口中。

您需要保留对您的actionSheet的引用,这是您能做的最好的事情。

您可以参考https://dev59.com/MWMl5IYBdhLWcg3wFjkC#19275311来实现。

Class UIAlertManager = NSClassFromString(@"_UIAlertManager");
UIAlertView *alertView = [UIAlertManager performSelector:@selector(topMostAlert)];

编辑:这是一个私有API。


我对此的担忧在于你放置链接中的评论:“我不确定它是否可以通过AppStore的审核,但是可以运行”。难道AppStore会拒绝这种方法吗? - Goca
2
@MoisesCardenas: 他们可能会这么做,因为它使用了私有类UIAlertManager(和_UIAlertManager)。你可能需要编写自己的UIAlertManager,只需存储UIAlertView的实例。 - Scott Berrevoets
1
如果您想要发布您的应用程序,请不要使用此代码。 _UIAlertManagerUIKit 框架的私有类。 - Gabriele Petronella
我给你一个赞,因为“你需要保留对你的 actionSheet 的引用,这是你能做的最好的事情。” 另一方面,我不喜欢做那些未经 App Store 批准的事情(如果你将不得不再次做一遍,那么这是浪费时间),所以你应该添加一个大的注释,说明我的应用程序将因使用它而被拒绝。 也许它对测试目的有用,所以感谢你的回答。 - Goca
人们因使用此链接而被拒绝:http://stackoverflow.com/questions/22219706/the-app-references-non-public-selectors-in-payload-appname-app-app-name-fai/22220535#22220535 - Rafał Sroka
显示剩余3条评论

3
与其使用O(n^2)的方法关闭警报,创建私有属性并通过它们的合成getter引用和解除对它们的引用可能更加轻量级(并且符合iOS 7)。另外,我有时会在alertview上设置标签,并通过标签引用它作为一个快速而简单的解决方案。
如果这些解决方案对于你的应用程序来说太简单了,我建议重新考虑你对alertview的使用。太多的应用程序滥用alertview,在我看来,它们应该被非常节制地使用 - 只是为了添加一些未经请求的反馈 :)。
另一种可能有助于你的方法是在alertview生命周期完成时实现基于块的回调。请参见Simplify UIAlertView with Blocks

1

另一种跟踪可见UIAlertView实例的方法是使用方法交换:

UIAlertView+Dismiss.h:

#import <UIKit/UIKit.h>

@interface UIAlertView (Dismiss)

+ (void)dismissAllVisibleAlertViews;

@end

UIAlertView+Dismiss.m:

#import "UIAlertView+Dismiss.h"
#import <objc/runtime.h>

// see http://nshipster.com/method-swizzling/
static inline void swizzle(Class class, SEL originalSelector, SEL swizzledSelector)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@implementation UIAlertView (Dismiss)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        swizzle([self class], @selector(show), @selector(xxx_show));
        swizzle([self class], @selector(dismissWithClickedButtonIndex:animated:), @selector(xxx_dismissWithClickedButtonIndex:animated:));
    });
}

+ (void)dismissAllVisibleAlertViews
{
    for (NSValue *value in [self visibleAlertViews])
    {
        id val = value.nonretainedObjectValue;

        if ([val isKindOfClass: [UIAlertView class]])
        {
            [val dismissWithClickedButtonIndex: 0 animated: YES];
        }
    }
}

#pragma mark - Method Swizzling

- (void)xxx_show
{
    [self xxx_show];

    [[self.class visibleAlertViews] addObject: [NSValue valueWithNonretainedObject: self]];
}

- (void)xxx_dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
    [self xxx_dismissWithClickedButtonIndex: buttonIndex animated: animated];

    [[self.class visibleAlertViews] removeObject: [NSValue valueWithNonretainedObject: self]];
}

#pragma mark - Cache

+ (NSMutableSet *)visibleAlertViews
{
    static NSMutableSet *views = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        views = [NSMutableSet new];
    });

    return views;
}

@end

这个方法能够正常工作是因为通过调用show方法来显示UIAlertViews。然后,替换的方法会跟踪该实例,直到通过dismissWithClickedButtonIndex:animated:方法关闭它。您可以通过调用[UIAlertView dismissAllVisibleAlertViews];轻松地关闭所有警报视图。

1

Xcode 6.4, 适用于iOS8.4版本,启用ARC

有许多关于此主题的帖子。但是,似乎没有一个对我来说是清晰的解决方案,因此我花了几个小时进行测试,并最终提供了一个解决OP问题的解决方案:

"我试图在显示另一个UIAlertView之前关闭一个UIAlertView..."

正如OP所述,".windows" 方法将不再起作用。还有其他一些方法涉及创建UIAlertView类别和使用通知,但对我而言都过于复杂。

以下是解决方法...

1) 使您的类符合UIAlertViewDelegate协议。

在您的类的“*.h”文件中...

@interface YourViewController : UIViewController <UIAlertViewDelegate>

这将使您的类中的UIAlertView对象能够向以下方法发送消息:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex

并且你的类中的UIAlertView对象需要从以下方法接收消息:

- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated:

聪明的人会知道,在某些情况下,您不必将您的类符合UIAlertViewDelegate的标准,但这是更安全的选择。这完全取决于您在类中如何使用对象。

2)将UIAlertView对象声明为类变量或属性。

创建属性的一些优点是,您可以访问对象的一些getter和setter。

作为实例变量,在您的类的“*.h”文件中...

@interface YourViewController : UIViewController <UIAlertViewDelegate>
{
   UIAlertView *yourAlertView;
{

//other properties

@end

在您的类的“*.h”文件中,属性(推荐)
@interface YourViewController : UIViewController <UIAlertViewDelegate>
{
   //other instance variables
{

@property (strong, nonatomic) UIAlertView *yourAlertView;

@end

3) 避免生成多个对UIAlertView对象的引用。

例如,如果您有一个监视某个条件并显示警报的方法,则不要每次实例化UIAlertView对象。相反,在-(void)viewDidLoad中实例化一次,并在需要时使用它。否则,这将防止

- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated:

将所需消息发送到正确的UIAlertView对象的方法。

4)将标签分配给UIAlertView对象,并操作属性以更改标题、消息等。

self.yourAlertView.title = @"some title string";
self.yourAlertView.message = @"some message string";

5) 显示UIAlertView对象。

[self.yourAlertView show];

6) 在显示更改后的UIAlertView对象之前,先将其关闭。

self.yourAlertView.title = @"some other title string";
self.yourAlertView.message = @"some other message string";

[self.yourAlertView show];

7) UIAlertView在iOS8中已被弃用。

https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html#//apple_ref/doc/uid/TP40006802-CH3-SW8

重要提示:UIAlertView在iOS 8中已被弃用。(请注意,UIAlertViewDelegate也已被弃用。)在iOS 8及更高版本中创建和管理警报,请改用preferredStyle为UIAlertControllerStyleAlert的UIAlertController。
在运行于iOS 8之前的版本的应用程序中,使用UIAlertView类向用户显示警报消息。警报视图类似于但外观不同于操作表(UIActionSheet的实例)。
使用此类中定义的属性和方法设置警报视图的标题、消息和委托,并配置按钮。如果添加自定义按钮,则必须设置委托。委托应符合UIAlertViewDelegate协议。使用show方法在配置完毕后显示警报视图。

0

我曾经遇到过同样的问题,但我不想将所有可能的警告视图保存为属性。我在这里找到了一个很好的替代方案here

UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Alert!" message:@"This alert will dismiss when application resigns active!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){
    [alert dismissWithClickedButtonIndex:0 animated:NO];
}];

在同一线程中,作为对其中一篇文章的评论,有一个解释为什么旧方法在iOS 7中不起作用:
“在iOS7中,窗口不包含警报视图窗口。它们由另一堆栈的窗口管理,这些窗口没有暴露出来。”
希望能帮助其他人。 :)

0

关闭所有警告视图的代码。这是私有API,因此当您上传到Appstore时,您的应用可能会被苹果拒绝:

Class UIAlertManager = objc_getClass("_UIAlertManager");
    UIAlertView *topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)];
    while (topMostAlert) {
        [topMostAlert dismissWithClickedButtonIndex:0 animated:NO];
        topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)];
    }

1
你应该提供一个不会被苹果拒绝的解决方案。这样做的目的是为了得到最佳答案,如果应用程序无法进入商店,那么这不是一个好的实践。 - Goca
因为我认为有人只需要在测试时取消AlertView。或者有人不想提交应用程序到Appstore。 - huync

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接