代码之家  ›  专栏  ›  技术社区  ›  François P.

进入后台状态时取消UIAlertViews

  •  31
  • François P.  · 技术社区  · 14 年前

    苹果公司建议驳回任何 UIAlertViews/UIActionSheets 在ios4中进入后台状态时。这是为了避免用户在以后重新启动应用程序时出现任何混乱。我想知道我怎么能优雅地一次删除所有UIAlertView,而不保留每次设置一个视图时对它的引用。。。

    你知道吗?

    12 回复  |  直到 10 年前
        1
  •  24
  •   Community Nick Dandoulakis    7 年前

    我很好奇 Dad's answer (有趣的用户名:),很好奇为什么会被否决。

    下面是UIAlertView子类的.m部分。

    编辑: (Cdric)我添加了一种方法来捕获对委托方法的调用,然后删除观察者,以避免多次注册到通知中心。

    在这个github repo中捆绑在一个类中的所有内容: 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
    
    

    这很好,因为您可以像使用UIAlertView一样开始使用它。

    我还没来得及彻底测试它,但我没有注意到任何副作用。

        2
  •  26
  •   Mark Coleman    12 年前

    - (void) hide {
      [self dismissWithClickedButtonIndex:0 animated:YES];
    }
    

    和托付给 UIApplicationWillResignActiveNotification

    [[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];
    
        3
  •  18
  •   auco    10 年前

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

    应用程序委托的递归函数

    - (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];
            }
        }
    }
    

    [self checkViews:application.windows];
    
        4
  •  12
  •   Dad    14 年前

    呵呵。我还没有尝试过这个,但是我想知道创建一个UIAlertView的子类是否有意义,它可以监听这个通知并关闭它自己,如果是这样的话。。。

        5
  •  12
  •   pIkEL    11 年前

    正如有人在评论中提到的:接受的答案并不是自ios4.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];
        }];
    
        6
  •  8
  •   jcady    8 年前

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

    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)
            }
        }
    }
    

    然后在AppDelegate中:

    func applicationDidEnterBackground(application: UIApplication)
    {
        UIApplication.dismissOpenAlerts()
    }
    
        7
  •  7
  •   Ponja    12 年前

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

    /* 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];
        }
    }
    
        8
  •  3
  •   GorillaPatch    14 年前

    最简单的方法是保存对UIAlertView的引用,这样就可以取消它。当然,正如petert提到的,您可以通过通知来完成,或者在UIApplication上使用delegate方法

    applicationWillResignActive:
    

    因此,当您真正进入后台时,我会关闭UIAlertView并在委托调用中保存状态:

    applicationDidEnterBackground:
    

    在developer.apple.com上免费查看会话105-在WWDC10的iOS4上采用多任务处理。16:00开始变得有趣

    graphic 了解应用程序的不同状态

        9
  •  1
  •   petert    14 年前

    UIApplicationWillResignActiveNotification (请参阅UIApplication)在具有UIAlertView之类内容的视图中,您可以通过以下方式以编程方式删除警报视图:

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

    对这个方法的讨论甚至暗示了它在iOS4中的用途!

    在iPhoneOS4.0中,当应用程序移到后台时,您可能希望调用此方法。当应用程序移到后台时,不会自动取消警报视图。此行为与以前版本的操作系统不同,以前版本的操作系统在应用程序终止时自动取消。解除警报视图使应用程序有机会保存更改或中止操作,并执行任何必要的清理,以防以后终止应用程序。

        10
  •  0
  •   Mark Coleman    12 年前

    assign

    @property (nonatomic, assign) UIAlertView* alertview;
    

    然后,在app delegate中:

    [self.viewController.alertview dismissWithClickedButtonIndex:[self.viewController.alertview cancelButtonIndex] animated:NO];
    

    你可以把这个放进去 applicationDidEnterBackground: 或者任何你认为合适的地方。它在应用程序退出时以编程方式关闭警报。我一直在这样做,效果很好。

        11
  •  0
  •   Rohit Ragmahale    10 年前

    在UIAlert视图上创建类别

    使用 http://nshipster.com/method-swizzling/

    - 如果要删除所有数据,请对已保存的警报视图调用dissible并清空数组。

        12
  •  0
  •   Community Nick Dandoulakis    7 年前

    另一种解决方案,基于普克尔的, answer ,当应用程序放在后台时,观察者将被移除。如果用户通过按下一个按钮来解除警报,观察者仍将处于活动状态,但只有在将应用程序放在后台(在后台运行程序块时,显示“nil alertView”,并移除观察者)时才会激活。

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:message
                                                   delegate:alertDelegate
                                          cancelButtonTitle:cancelButtonText
                                          otherButtonTitles:okButtonText, nil];
       [alert show];
    
       __weak UIAlertView *weakAlert = alert;
       __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:      [NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){
       [weakAlert dismissWithClickedButtonIndex:[weakAlert cancelButtonIndex] animated:NO];
       [[NSNotificationCenter defaultCenter] removeObserver:observer];
        observer = nil;
       }];