Dismissing UIAlertViews when entering background state

前端 未结 12 1561
心在旅途
心在旅途 2020-12-02 06:44

Apple recommends dismissing any UIAlertViews/UIActionSheets when entering background state in iOS 4. This is to avoid any confusion on the user\'s part when he

相关标签:
12条回答
  • 2020-12-02 07:16

    I was intrigued by Dad's answer (funny username :), and curious why it was down-voted.

    So I tried it.

    Here is the .m part of a subclass of UIAlertView.

    Edit: (Cédric) I have added a way to catch calls to delegate methods and remove the observer then to avoid multiple registrations to the notification center.

    Everything bundled in a class in this 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
    
    

    It works nicely. It's great, because you can just start using it the same way that you used to use UIAlertView.

    I haven't had time to test it thoroughly, but I didn't notice any side effect.

    0 讨论(0)
  • 2020-12-02 07:17

    My call would be to add a category to UIAlertview adding the following function :

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

    And to suscribe to UIApplicationWillResignActiveNotification :

    [[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];
    
    0 讨论(0)
  • 2020-12-02 07:17

    if you only have one or two specific alert windows you show (as do most apps), then you can just create an assign ivar to the alert:

    @property (nonatomic, assign) UIAlertView* alertview;
    

    Then, in the app delegate:

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

    You can put this in applicationDidEnterBackground: or wherever you see fit. It closes the alert programmatically upon application exit. I've been doing this and it works great.

    0 讨论(0)
  • 2020-12-02 07:22

    huh. Haven't tried this yet, but I wonder if it would make sense to create a subclass of UIAlertView that listens for this Notification and closes itself if so...

    That'd have the "automatically" without retaining / keeping it around characteristic OP is requesting. Make sure to unregister for the notification on close (else boom!)

    0 讨论(0)
  • 2020-12-02 07:23

    Create category on UIAlert View

    Use http://nshipster.com/method-swizzling/ Swizzle "show" method

    Keep track of alert view shown by keeping week references in array.

    - When you want to remove all data call Dismiss on saved alert views and empty an array.

    0 讨论(0)
  • 2020-12-02 07:23

    An alternative solution, based on plkEL's, answer, where the observer is removed when the app is put in the background. If user dismisses the alert by pressing a button, the observer will still be active, but only until the app is put in the background (where the block is run - with an "nil alertView" - and the observer removed).

        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;
       }];
    
    0 讨论(0)
提交回复
热议问题