Pause code execution until UIAlertView button is clicked?

One of my methods sends a message to the object (what you know about it), and awaits a BOOL response. However, the BOOL response that he expects is based on a response to the UIAlertView created in the method of the receiving object. However, the code does not stop waiting for the user to respond to the UIAlertView. My problem: how can I use -alertView: clickedButtonAtIndex in the returned method?

Here's the code the message is running in (in this construct, I expected navigateAwayFromTab to change based on user input in the UIAlertView, but never gets a 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; } 

I read about the modal discussions of UIAlertView, and I agree with the apple implementation - as that is the rule. However, in this case, I see no way to solve the problem by putting the code in -alertView: clickedButtonAtIndex, because I do not need to run the code based on UIAlertView, I just need to read the answer. Any suggestions on how I can get to my prison? I already tried the while loop after [alert show], but then the warning does not even appear, and for a number of reasons I can’t use -viewWillDisapear.

Edit

For those who are considering this issue in the modern era, this question refers to ios 2

+4
source share
4 answers

Not only does the UIAlertView display not wait until the user touches the button, he did not even wait for the warning to appear. It returns immediately .

Add a flag to your class. If it is NO, return NO from readyToNavigateAwayFromTab and show your warning. In clickedButtonAtIndex, set the flag so that the readyToNavigateAwayFromTab command knows that it returns YES. Still in clickedButtonAtIndex, repeat the tab navigation from the code.

+10
source

Solution using NSCondition when starting from a background thread:

 // 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

+4
source

The best solution I have found is Sasmito Adibowo on my Painless UIAlertView blog. He created BSAlertViewDelegateBlock, a general implementation of UIAlertViewDelegate that allows you to use closure as a delegate handler.

Here's how you use it:

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

Implementations of BSAlertViewDelegateBlock.h and BSAlertViewDelegateBlock.m are available on GitHub, but I rewrote the code here:

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 
+1
source

The [alert show] statement must support the application until a response is provided.

Have you connected your controller to the UIAlertViewDelegate protocol? Check if you need to add <UIAlertViewDelegate> to the controller header file, for example:

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

You can also make your navigateAwayFromTab parameter value, for example:

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

In implementation:

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

All Articles