进入后台状态时关闭UIAlertViews

Fra*_* P. 31 background uialertview uiactionsheet ios4 ios

Apple建议UIAlertViews/UIActionSheets在iOS 4中进入后台状态时解除任何一个状态.这是为了避免用户在以后重新启动应用程序时出现任何混淆.我想知道如何能够立刻优雅地解雇所有UIAlertViews,而不是每次我设置它时都不保留对它的引用...

任何的想法 ?

小智 26

我的电话是为UIAlertview添加一个类别,添加以下功能:

- (void) hide {
  [self dismissWithClickedButtonIndex:0 animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

并承认UIApplicationWillResignActiveNotification :

[[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];
Run Code Online (Sandbox Code Playgroud)

  • 谢谢这让我对如何实现我想要的东西有了一个好主意.我可能会添加两个方法在一个类别 - (无效)showAndHidAutomatically,这将自身注册和呼叫显示,在该通知会打电话叫hideAndUnregister另一种方法,这将做到这一点.享受赏金奖励:-) (3认同)

Gui*_*ume 24

我被爸爸的回答(有趣的用户名:) 所吸引,并且好奇为什么会被投票.

所以我试了一下.

这是UIAlertView子类的.m部分.

编辑:(Cédric)我添加了一种方法来捕获委托方法的调用并删除观察者然后避免多次注册到通知中心.

在这个github仓库中捆绑的所有东西:https://github.com/sdarlington/WSLViewAutoDismiss



    #import "UIAlertViewAutoDismiss.h"
    #import <objc/runtime.h>

    @interface UIAlertViewAutoDismiss () <UIAlertViewDelegate> {
        id<UIAlertViewDelegate> __unsafe_unretained privateDelegate;
    }
    @end

    @implementation UIAlertViewAutoDismiss

    - (id)initWithTitle:(NSString *)title
                message:(NSString *)message
               delegate:(id)delegate
      cancelButtonTitle:(NSString *)cancelButtonTitle
      otherButtonTitles:(NSString *)otherButtonTitles, ...
    {
        self = [super initWithTitle:title
                            message:message
                           delegate:self
                  cancelButtonTitle:cancelButtonTitle
                  otherButtonTitles:nil, nil];

        if (self) {
            va_list args;
            va_start(args, otherButtonTitles);
            for (NSString *anOtherButtonTitle = otherButtonTitles; anOtherButtonTitle != nil; anOtherButtonTitle = va_arg(args, NSString *)) {
                [self addButtonWithTitle:anOtherButtonTitle];
            }
            privateDelegate = delegate;
        }
        return self;
    }

    - (void)dealloc
    {
        privateDelegate = nil;
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
        [super dealloc];
    }

    - (void)setDelegate:(id)delegate
    {
        privateDelegate = delegate;
    }

    - (id)delegate
    {
        return privateDelegate;
    }

    - (void)show
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationDidEnterBackground:)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];

        [super show];
    }

    - (void)applicationDidEnterBackground:(NSNotification *)notification
    {
        [super dismissWithClickedButtonIndex:[self cancelButtonIndex] animated:NO];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
    }

    #pragma mark - UIAlertViewDelegate

    // The code below avoids to re-implement all protocol methods to forward to the real delegate.

    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        struct objc_method_description hasMethod = protocol_getMethodDescription(@protocol(UIAlertViewDelegate), aSelector, NO, YES);
        if (hasMethod.name != NULL) {
            // The method is that of the UIAlertViewDelegate.

            if (aSelector == @selector(alertView:didDismissWithButtonIndex:) ||
                aSelector == @selector(alertView:clickedButtonAtIndex:))
            {
                [[NSNotificationCenter defaultCenter] removeObserver:self
                                                                name:UIApplicationDidEnterBackgroundNotification
                                                              object:nil];
            }
            return privateDelegate;
        }
        else {
            return [super forwardingTargetForSelector:aSelector];
        }
    }

    @end

Run Code Online (Sandbox Code Playgroud)

它工作得很好.这很棒,因为您可以像以前使用UIAlertView一样开始使用它.

我没有时间彻底测试它,但我没有注意到任何副作用.

  • 如果有人想要使用这样的代码,我已经在github上创建了一个实现它的存储库以及UIActionSheet的等价物:http://github.com/sdarlington/UIViewAutoDismiss (3认同)

小智 19

一种完全不同的方法是递归搜索.

应用程序委托的递归函数

- (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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

从applicationDidEnterBackground过程调用它

[self checkViews:application.windows];
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这不再适用于iOS7,因为UIalertviews不再是应用程序窗口之一的子视图 (7认同)

Dad*_*Dad 12

呵呵.还没有尝试过这个,但是我想知道创建一个UIAlertView的子类是否有意义,它会监听这个Notification并关闭它自己......

这有"自动"而没有保留/保持特征OP正在请求.确保在关闭时取消注册通知(否则繁荣!)


pIk*_*kEL 12

正如评论中提到的那样:当我们拥有块时,接受的答案不是iOS 4.0以来最好/最干净的答案!我是这样做的:

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];
    }];
Run Code Online (Sandbox Code Playgroud)


jca*_*ady 8

UIAlertView在iOS 8中被弃用,转而使用UIAlertController.不幸的是,这被证明是一个棘手的问题,因为接受的解决方案不起作用,因为Apple显然不支持子类化UIAlertController:

UIAlertController类旨在按原样使用,不支持子类化.此类的视图层次结构是私有的,不得修改.

我的解决方案是简单地遍历视图控制器树并关闭您找到的所有UIAlertControllers.您可以通过创建UIApplication的扩展然后在AppDelegate applicationDidEnterBackground方法中调用它来全局启用它.

试试这个(在Swift中):

extension UIApplication
{
    class func dismissOpenAlerts(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController)
    {
        //If it's an alert, dismiss it
        if let alertController = base as? UIAlertController
        {
            alertController.dismissViewControllerAnimated(false, completion: nil)
        }

        //Check all children
        if base != nil
        {
            for controller in base!.childViewControllers
            {
                if let alertController = controller as? UIAlertController
                {
                    alertController.dismissViewControllerAnimated(false, completion: nil)
                }
            }
        }

        //Traverse the view controller tree
        if let nav = base as? UINavigationController
        {
           dismissOpenAlerts(nav.visibleViewController)
        }
        else if let tab = base as? UITabBarController, let selected = tab.selectedViewController
        {
           dismissOpenAlerts(selected)
        }
        else if let presented = base?.presentedViewController
        {
           dismissOpenAlerts(presented)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的AppDelegate中:

func applicationDidEnterBackground(application: UIApplication)
{
    UIApplication.dismissOpenAlerts()
}
Run Code Online (Sandbox Code Playgroud)


Pon*_*nja 7

我用以下代码解决了这个问题:

/* taken from the post above (Cédric)*/
- (void)checkViews:(NSArray *)subviews {
    Class AVClass = [UIAlertView class];
    Class ASClass = [UIActionSheet class];
    for (UIView * subview in subviews){
        NSLog(@"Class %@", [subview class]);
        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];
        }
    }
}



/*go to background delegate*/
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    for (UIWindow* window in [UIApplication sharedApplication].windows) {
        NSArray* subviews = window.subviews;
        [self checkViews:subviews];
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 遗憾的是,这不再适用于iOS7,因为UIAlertViews无法作为应用程序窗口的子视图 (4认同)