29 votes

Fermeture des UIAlertViews lors du passage en arrière-plan

Apple recommande de rejeter toute UIAlertViews/UIActionSheets lors de l'entrée dans l'état d'arrière-plan dans iOS 4. Ceci afin d'éviter toute confusion de la part de l'utilisateur lorsqu'il relance l'application ultérieurement. Je me demande comment je pourrais licencier élégamment toutes les UIAlertViews en même temps, sans conserver une référence à chaque fois que j'en crée une...

Une idée ?

23voto

Guillaume Points 11397

J'ai été intrigué par La réponse de papa (nom d'utilisateur amusant :), et curieux de savoir pourquoi il a été déclassé.

Alors j'ai essayé.

Voici la partie .m d'une sous-classe de UIAlertView.

Edit : (Cédric) J'ai ajouté un moyen d'attraper les appels aux méthodes déléguées et de supprimer l'observateur ensuite pour éviter les enregistrements multiples au centre de notification.

Tout est regroupé dans une classe dans ce dépôt 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

Il fonctionne bien. C'est génial, car vous pouvez commencer à l'utiliser de la même manière que vous utilisiez UIAlertView.

Je n'ai pas eu le temps de le tester à fond, mais je n'ai pas remarqué d'effet secondaire.

22voto

Charter Points 713

Mon appel serait d'ajouter une catégorie à UIAlertview en ajoutant la fonction suivante :

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

Et pour s'abonner à UIApplicationWillResignActiveNotification :

[[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];

19voto

Wilbert Points 191

Une approche totalement différente est la recherche récursive.

Fonction récursive pour le délégué de votre application

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

L'appeler depuis la procédure applicationDidEnterBackground

[self checkViews:application.windows];

12voto

Dad Points 3898

Huh. Je n'ai pas encore essayé, mais je me demande s'il ne serait pas judicieux de créer une sous-classe de UIAlertView qui écoute cette notification et se ferme si c'est le cas...

Cela aurait le "automatiquement" sans retenir / garder autour de la caractéristique OP demande. Assurez-vous de vous désinscrire pour recevoir la notification à la fermeture (sinon, boum !).

9voto

pIkEL Points 96

Comme quelqu'un l'a mentionné dans un commentaire : la réponse acceptée n'est pas la meilleure/la plus propre depuis iOS 4.0 où nous avons des blocs ! Voici comment je procède :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X