Monday, April 16, 2012

Dismissing UIAlertViews when entering background state


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 relaunches the application later. I wonder how I could elegantly dismiss all UIAlertViews at once, without retaining a reference to it everytime I set one up...



Any idea ?


Source: Tips4all

7 comments:

  1. 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.

    #import "UIAlertViewAutoDismiss.h"


    @implementation UIAlertViewAutoDismiss


    - (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id /**/)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {

    if ((self = [super initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil])) {

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

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    }
    return self;
    }


    - (void) dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
    }

    - (void) applicationDidEnterBackground:(NSNotification*) notification {
    // We should not be here when entering back to foreground state
    [self dismissWithClickedButtonIndex:[self cancelButtonIndex] animated:NO];
    }

    @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.

    ReplyDelete
  2. A totally different approach is a recursive search.

    Recursive function for your application delegate

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


    Calling it from the applicationDidEnterBackground procedure

    [self checkViews:application.windows];

    ReplyDelete
  3. 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!)

    ReplyDelete
  4. 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];

    ReplyDelete
  5. The straightforward way is to hold a reference to the UIAlertView so you can dismiss it. Of course as petert mentioned you can do it with a Notification or use the delegate method on UIApplication

    applicationWillResignActive:


    does not always mean that you are going to the background. You will for example also receive that delegate call and notification (you get both) when the user gets a phone call or receives and SMS. So you have to decide what should happen if the user gets an SMS and presses cancel to stay in your app. You maybe want to make sure that your UIAlertView is still there.

    So I would dismiss the UIAlertView and save the state in the delegate call when you really go into the background:

    applicationDidEnterBackground:


    Have a look at Session 105 - Adopting Multitasking on iOS4 of WWDC10 available for free at developer.apple.com. It gets interesting at 16:00 min

    Check out this graphic to understand the different states of an application

    ReplyDelete
  6. I have this on my TODO list, but my first instinct would be to listen out for the notifcation UIApplicationWillResignActiveNotification (see UIApplication) in the views where you have things like UIAlertView - here you can programmatically remove the alert view with:

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


    The discussion for this method even suggests what it's for in iOS4!


    In iPhone OS 4.0, you may want to call this method whenever your application moves to the background. An alert view is not dismissed automatically when an application moves to the background. This behavior differs from previous versions of the operating system, where they were canceled automatically when the application was terminated. Dismissing the alert view gives your application a chance to save changes or abort the operation and perform any necessary cleanup in case your application is terminated later.

    ReplyDelete
  7. 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.

    ReplyDelete