2009-09-03 6 views
4

Une de mes méthodes envoie un message à un objet (que savez-vous à ce sujet), et attend un BOOL pour une réponse. Toutefois, la réponse BOOL attendue est basée sur la réponse à une UIAlertView créée dans la méthode de l'objet récepteur. Cependant, le code ne s'arrête pas en attendant que l'utilisateur réponde à UIAlertView. Mon problème est: comment utiliser -alertView: clickedButtonAtIndex dans la valeur de retour de la méthode?Pause de l'exécution du code jusqu'à ce que le bouton UIAlertView soit enfoncé?

Voici le code du message court (dans cette construction, je me attendais navigateAwayFromTab à changer en fonction de l'entrée utilisateur dans le UIAlertView, mais il ne fait jamais une chance):

- (BOOL)readyToNavigateAwayFromTab { 
    NSLog(@"message received by Medical View"); 
    navigateAwayFromTab = NO; 
    UIAlertView *alert = [[UIAlertView alloc] 
      initWithTitle:@"Navigate Away From Tab?" 
       message:@"Navigating away from this tab will save your work." 
       delegate:self 
     cancelButtonTitle:@"Cancel" 
     otherButtonTitles:@"OK", nil 
    ]; 
    [alert show]; 
    [alert release]; 
    return navigateAwayFromTab; 
} 
#define CANCEL 0 
#define OK 1 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { 
    if(buttonIndex == OK) navigateAwayFromTab = YES; 
} 

J'ai lu sur le débat modal UIAlertView, et je suis d'accord avec la mise en œuvre d'Apple - au moins comme la règle. Cependant, dans ce cas, je ne vois aucun moyen de résoudre le problème en mettant du code dans -alertView: clickedButtonAtIndex car je n'ai pas besoin d'exécuter du code basé sur le UIAlertView, j'ai juste besoin de lire la réponse. Des suggestions sur comment je peux atteindre ma prison? J'ai déjà essayé une boucle while après [show d'alerte], mais l'alerte ne s'affiche même pas, et pour un certain nombre de raisons, je ne peux pas utiliser -viewWillDisapear.

Modifier

Pour ceux qui regardent cette question à l'époque ios moderne, cette question avaient trait à ios 2

Répondre

10

Non seulement UIAlertView ne montre pas attendre que l'utilisateur touche un bouton, mais il n'attend même pas que la vue d'alerte apparaisse. Il renvoie immédiatement.

Ajoutez un drapeau à votre classe. Si c'est NON, renvoyez NO de readyToNavigateAwayFromTab et affichez votre alerte. Dans clickedButtonAtIndex, définissez l'indicateur de sorte que readyToNavigateAwayFromTab sache retourner YES. Toujours dans clickedButtonAtIndex, réessayez la navigation par tabulation à partir du code.

+0

Solution brillante. Vive les drapeaux! Merci. – JoBu1324

+0

Wow. Je n'aurais jamais imaginé que tu reviendrais, ma réponse était si tardive. Content que cela ait aidé. :) –

0

La déclaration [alert show] devrait pouvoir tenir l'application jusqu'à ce qu'une réponse est fournie.

Avez-vous fait en sorte que votre contrôleur s'abonne au protocole UIAlertViewDelegate? Vérifiez si vous avez besoin d'ajouter <UIAlertViewDelegate> à votre fichier d'en-tête du contrôleur, par exemple:

@interface RootViewController : UIViewController <UIAlertViewDelegate, UITabBarDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> 

Vous pouvez également faire votre variable navigateAwayFromTab une propriété, par exemple:

@interface RootViewController : UIViewController <UIAlertViewDelegate> { 
    BOOL navigateAwayFromTab; 
} 

@property BOOL navigateAwayFromTab; 

... 

@end 

Dans la mise en œuvre:

@implementation RootViewController 

@synthesize navigateAwayFromTab; 

... 

- (void) readyToNavigateAwayFromTab { 
    UIAlertView *alert = [[UIAlertView alloc] 
     initWithTitle:@"Navigate Away From Tab?" 
      message:@"Navigating away from this tab will save your work." 
      delegate:self 
    cancelButtonTitle:@"Cancel" 
    otherButtonTitles:@"OK", nil 
    ]; 
    [alert show]; 
    [alert release]; 
} 

#define CANCEL 0 
#define OK 1  
- (void) alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { 
    if ([actionSheet.title compare:@"Navigate Away From Tab?"] == NSOrderedSame) { 
    if (buttonIndex == OK) 
     self.navigateAwayFromTab = YES; 
    else 
     self.navigateAwayFromTab = NO; 
    } 
} 
+0

Merci pour la réponse! Je me suis abonné à UIAlertViewDelegate sans aucun effet. Êtes-vous sûr (e) que [alerte] est supposé attendre que l'alerte disparaisse? Partout où j'ai regardé, j'ai vu des gens essayer de faire agir UIAlertView de manière modale. – JoBu1324

+0

Si [alerte] ne fonctionne pas correctement, je suggère de créer un nouveau projet dans XCode et de faire un test UIAlertView dans ce projet de test. C'est plus de travail, accordé, mais il vous aidera à résoudre vos problèmes existants. –

+0

Ok, j'ai pris le code pomme d'UICatalog et j'ai inséré un message NSLog() après leur [show d'alerte], mais le message NSLog a été déclenché avant de rejeter l'alerte. J'ai même placé un autre appel NSLog après que la méthode d'alerte a été appelée, mais ce message a également couru avant de rejeter l'alerte, donc toujours pas de joie. Je déteste avoir l'air douteux, mais je suis 0 pour 2 maintenant. Êtes-vous sûr (e) que [show d'alerte] devrait interrompre l'exécution du code dans la méthode dans laquelle il est appelé? Avez-vous du code que vous pouvez poster qui fonctionne comme vous le prétendez? Merci, jb – JoBu1324

4

Solution à l'aide NSCondition lors du déclenchement d'un fil de fond:

// on background thread 
condition = [NSCondition new]; 
questionAnswered = NO; 
// display UIAlertView in a method on main thread with -performSelectorOnMainThread:withObject:waitUntilDone: 
[condition lock]; 
while (! questionAnswered) [condition wait]; 
questionAnswered = NO; 
[condition unlock]; 
[condition release]; 

// on main thread in delegate method -alertView:clickedButtonAtIndex: 
// (do something with choosen buttonIndex) 
questionAnswered = YES; 
[condition signal]; 
[condition unlock] 

Raphael

+0

+1 parce que cela vous mène réellement sur la bonne voie qui consistait à créer votre propre classe AlertView qui appelle UIAlertView + MKBlockAdditions.h mais dans ma classe AlertView, j'ai enveloppé mon appel à MKUIAlertView dans le code de thread fourni ci-dessus. , seule chose que j'ai remarqué est que vous déverrouillez le fil plusieurs fois, ce qui provoque quelques erreurs. – J3RM

1

meilleure solution que j'ai trouver est de Sasmito Adibowo dans son billet de blog Painless UIAlertView. Il a créé BSAlertViewDelegateBlock, une implémentation UIAlertViewDelegate générique qui vous permet d'utiliser des fermetures en tant que gestionnaire de délégué.

Voici comment l'utiliser:

UIAlertView* alert = ...; 
BSAlertViewDelegateBlock* alertDelegate = [[BSAlertViewDelegateBlock alloc] initWithAlertView:alert]; 
alertDelegate.didDismissWithButtonIndexBlock = ^(UIAlertView* alertView, NSInteger buttonIndex) 
{ 
    switch (buttonIndex) 
    { 
     ... 
    } 
}; 
[alert show]; 

La mise en œuvre de BSAlertViewDelegateBlock.h et BSAlertViewDelegateBlock.m sont disponibles sur GitHub mais j'ai republié le code ici:

BSAlertViewDelegateBlock.h

#import <UIKit/UIKit.h> 

/** 
Adapts UIAlertViewDelegate protocol into a block-friendly interface. 
*/ 
@interface BSAlertViewDelegateBlock : NSObject<UIAlertViewDelegate> 

/** 
Designated initializer. 
*/ 
-(id) initWithAlertView:(UIAlertView*) alertView; 

@property (nonatomic,strong) void(^clickedButtonAtIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 
@property (nonatomic,strong) void(^alertViewCancelBlock)(UIAlertView* alertView); 
@property (nonatomic,strong) void(^willPresentAlertViewBlock)(UIAlertView* alertView); 
@property (nonatomic,strong) void(^didPresentAlertViewBlock)(UIAlertView* alertView); 

@property (nonatomic,strong) void(^willDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 
@property (nonatomic,strong) void(^didDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 

@property (nonatomic,strong) BOOL(^alertViewShouldEnableFirstOtherButtonBlock)(UIAlertView* alertView); 

@end 

BSAlertViewDelegateBlock.m

#import <objc/runtime.h> 

#import "BSAlertViewDelegateBlock.h" 

const char* const BSAlertViewDelegateBlockKey = "BSAlertViewDelegateBlockKey"; 

@interface BSAlertViewDelegateBlock() 

@property (nonatomic,weak) UIAlertView* alertView; 

@end 

@implementation BSAlertViewDelegateBlock 

-(void) alertViewDelegateBlock_cleanup 
{ 
UIAlertView* alertView = self.alertView; 
// remove all handler blocks in case one of these blocks inadvertendly caused a circular reference to this delegate object. 
self.clickedButtonAtIndexBlock = nil; 
self.alertViewCancelBlock = nil; 
self.willPresentAlertViewBlock = nil; 
self.didPresentAlertViewBlock = nil; 
self.willDismissWithButtonIndexBlock = nil; 
self.didDismissWithButtonIndexBlock = nil; 
self.alertViewShouldEnableFirstOtherButtonBlock = nil; 
// finally remove this delegate from the alert view 
if (alertView.delegate == self) { 
alertView.delegate = nil; 
objc_setAssociatedObject(alertView, BSAlertViewDelegateBlockKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 
} 

-(id) initWithAlertView:(UIAlertView *)alertView 
{ 
if (self = [super init]) { 
self.alertView = alertView; 
objc_setAssociatedObject(alertView, BSAlertViewDelegateBlockKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
alertView.delegate = self; 
} 
return self; 
} 


#pragma mark UIAlertViewDelegate 

// Called when a button is clicked. The view will be automatically dismissed after this call returns 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 
{ 
void(^clickedButtonAtIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.clickedButtonAtIndexBlock; 
if (clickedButtonAtIndexBlock) { 
clickedButtonAtIndexBlock(alertView,buttonIndex); 
} 
} 

// Called when we cancel a view (eg. the user clicks the Home button). This is not called when the user clicks the cancel button. 
// If not defined in the delegate, we simulate a click in the cancel button 
- (void)alertViewCancel:(UIAlertView *)alertView 
{ 
void(^alertViewCancelBlock)(UIAlertView* alertView) = self.alertViewCancelBlock; 
if (alertViewCancelBlock) { 
alertViewCancelBlock(alertView); 
} 
[self alertViewDelegateBlock_cleanup]; 
} 

- (void)willPresentAlertView:(UIAlertView *)alertView 
{ 
void(^willPresentAlertViewBlock)(UIAlertView* alertView) = self.willPresentAlertViewBlock; 
if (willPresentAlertViewBlock) { 
willPresentAlertViewBlock(alertView); 
} 
} 


- (void)didPresentAlertView:(UIAlertView *)alertView 
{ 
void(^didPresentAlertViewBlock)(UIAlertView* alertView) = self.didPresentAlertViewBlock; 
if (didPresentAlertViewBlock) { 
didPresentAlertViewBlock(alertView); 
} 
} 


- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex 
{ 
void(^willDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.willDismissWithButtonIndexBlock; 
if (willDismissWithButtonIndexBlock) { 
willDismissWithButtonIndexBlock(alertView,buttonIndex); 
} 
} 


- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 
{ 
void(^didDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.didDismissWithButtonIndexBlock; 
if (didDismissWithButtonIndexBlock) { 
didDismissWithButtonIndexBlock(alertView,buttonIndex); 
} 
[self alertViewDelegateBlock_cleanup]; 
} 


- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView 
{ 
BOOL(^alertViewShouldEnableFirstOtherButtonBlock)(UIAlertView* alertView) = self.alertViewShouldEnableFirstOtherButtonBlock; 
if (alertViewShouldEnableFirstOtherButtonBlock) { 
return alertViewShouldEnableFirstOtherButtonBlock(alertView); 
} 

// default to enable. 
return YES; 
} 


@end 
Questions connexes