tags:

views:

501

answers:

3

One of my methods sends a message to an object (what do you know about that), and expects a BOOL for an answer. However, BOOL answer it is expecting is based on the answer to a UIAlertView created in the receiving object's method. However, the code doesn't pause while waiting for the user to answer the UIAlertView. My problem is: how do I use -alertView:clickedButtonAtIndex in the method's return value?

Here's the code the message runs (in this construction, I was expecting navigateAwayFromTab to change based on the user input in the UIAlertView, but it 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've been reading up on the modal UIAlertView debate, and I agree with apple's implementation - at lest as the rule. However, in this case I don't see any way of solving the problem by putting code in -alertView:clickedButtonAtIndex because I don't need to run code based on the UIAlertView, I just need to read the response. Any suggestions on how I can reach my gaol? I've tried a while loop after [alert show] already, but then the alert doesn't even show then, and for a number of reasons I can't use -viewWillDisapear.

A: 

The [alert show] statement should hold up the application until a response is provided.

Have you made your controller subscribe to the UIAlertViewDelegate protocol? Check if you need to add <UIAlertViewDelegate> to your controller header file, e.g.:

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

You might also make your navigateAwayFromTab variable a property, e.g.:

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

@property BOOL navigateAwayFromTab;

...

@end

In the 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;
   }
}
Alex Reynolds
Thanks for the reply! I've subscribed to UIAlertViewDelegate to no effect. Are you sure [alert show] is supposed to wait till alert disappears? Everywhere I've looked I've seen people trying make UIAlertView act in a modal manner.
JoBu1324
If [alert show] is not working properly, I would suggest creating a new project in XCode and doing a test UIAlertView in this test project. This is more work, granted, but it will help you troubleshoot your existing problems.
Alex Reynolds
Ok, I took the apple code from UICatalog and inserted an NSLog() message after their [alert show], but the NSLog message fired before dismissing the alert. I even placed another NSLog call after the alert method was called, but that message also ran before dismissing the alert, so still no joy. I hate to sound doubtful, but I'm 0 for 2 right now. Are you sure [alert show] should pause execution of code in the method it's called in? Do you have some code you can post that works the way you claim? Thanks, jb
JoBu1324
The code above should work. I am using a variant of it for an app I'm writing right now, in fact, and it works as I describe. In your test application (not the UICatalog sample app) you might call [self readyToNavigateAwayFromTab] in -viewDidLoad to trigger the UIAlertView.
Alex Reynolds
Thanks for being so patient with me! What version of the iPhone OS are you using? I'm using 3.0.
JoBu1324
I'm using 3.1, but it shouldn't matter. The code I gave you worked under 2.2 and works under 3.1.
Alex Reynolds
+1  A: 

Not only does UIAlertView's show not wait for the user to touch a button, it doesn't even wait for the alert view to fade into view. It returns immediately.

Add a flag to your class. If it's NO, return NO from readyToNavigateAwayFromTab and show your alert. In clickedButtonAtIndex, set the flag so that readyToNavigateAwayFromTab knows to return YES. Still in clickedButtonAtIndex, retry the tab navigation from code.

Steven Fisher
Brilliant solution. Hurrah for flags! Thanks.
JoBu1324
Wow. I never imagined you'd return, my answer was so late. Glad it helped. :)
Steven Fisher
A: 

Solution using NSCondition when triggering 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

Raphael Schaad