views:

1972

answers:

3

I have a weird timing issue, it seems. I open a uiimagepicker as a modal view. When the user picks an image or I want to take the image, save it to a variable, then open an email interface as a modalview.

My problem is that I am calling dismissModalViewController on the imagepicker, then calling presentmodalviewcontroller for my email interface, but the imagepicker isn't going away in time for the email view to be presented. Is there a way to 'wait' for that line of code to complete?

(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
  if( [[info objectForKey:UIImagePickerControllerMediaType] isEqualToString:@"public.image"] ){
 [self dismissModalViewControllerAnimated:YES];
 imageFromCamera = [[UIImageView alloc] initWithImage:[info objectForKey:UIImagePickerControllerOriginalImage]];

  MFMailComposeViewController *mailView = [[MFMailComposeViewController alloc] init];
  mailView.mailComposeDelegate = self;
  [self presentModalViewController:mailView animated:YES];
  [mailView release];

}   
}

I'm pretty sure I've designed something wrong, but help me out if you can.

+2  A: 

You can use performSelector:withObject:withDelay: to wait for a given time to pass (create another method that has the code to do later.

However, this can introduce lots of subtle timing bugs, so I'd suggest using it for only the most extreme of cases.

I think you could probably do something with viewDidAppear: look for a flag that you've set in didFinishPicking… indicating that you're waiting for the imagePicker's animation to end. Since it's didAppear, the animations should be done by then.

Andrew Pouliot
Thanks, that was very helpful.
Alfonsol
+2  A: 

I've encountered a similar issue with dismissModalViewController and it has been driving me nuts. The only way I've been able to deal with it is by doing something like this, which is similar to what was suggested above. I don't like this solution but I haven't been able to figure out how to avoid it.

if ([self retainCount] == 1) 
  [self dismissModalViewControllerAnimated:YES];        
else {
  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
}

- (void)onTimer:(NSTimer*)theTimer {
    [self dismissModalViewControllerAnimated:YES];
    [theTimer invalidate];
}

The problem I noticed is that here is a timing issue surrounding when some object releases its hold on the modal view controller. And if I call dismissModalViewController when the retainCount is still 2 then the call just fails...nothing happens. But if I wait a second, the retain count always drops down to 1 and then the call to dismissModalViewController succeeds.

Francois
This worked for me. Thanks!
NiKUMAN
A: 

While the transition is happening, all views reside on an intermediate view (of type UITransitionView). So just pick some outlet of which you know that it is a direct subview of the main window and check whether !([[outlet superview] isKindOfClass:[UIWindow class]]) and delay the execution using performSelector:withObject:withDelay:, pass all the relevant information to call the same method you’re in and simply return.

As soon as the transition is finished, the condition will not be met anymore and the new animation can happen. This method is not prone to the timing complexities which can happen if you just call performSelector:withObject:withDelay: once.

I used this just recently and it works very well (I just happened to have an outlet to the main window which makes this even simpler):

//Called after [initialRootViewController dismissModalViewControllerAnimated:YES]
- (void)showTable {
    if([initialRootViewController.view superview] != window) {
        //View is still animating
        [self performSelector:@selector(showTable) withObject:nil afterDelay:0.1];
        return;
    }
    self.nibContents = [[NSBundle mainBundle] loadNibNamed:@"MainView" owner:self options:nil];
    [UIView transitionFromView:initialRootViewController.view toView:rootViewController.view duration:0.3 options:UIViewAnimationOptionTransitionCurlUp|UIViewAnimationOptionBeginFromCurrentState completion:^(BOOL finished){
        self.initialRootViewController = nil;
    }];
}
Raphael Schweikert