7

I have what I believe is a fairly simple application at the moment based on a few tutorials cobbled together. I'm using XCode 3.2.3 in OSX 10.6.4. It started as a standard iPhone "Window Based Application". Using interface builder I have added a Tab Bar Controller using the O'Reilly video tutorial here:

http://broadcast.oreilly.com/2009/06/tab-bars-and-navigation-bars-t.html

In the first Tab I have a standard UIView with two buttons. Both call the same function to display a UIImagePickerController:

-(IBAction) btnPhotoClicked:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
if((UIButton *)sender == btnChoosePhoto)
{
    imagePicker.allowsEditing = YES;
    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
} else {
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}

[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}

I am running the code inside an emulator so only ever click the button called Choose Photo. When the dialogue is released with a photo chosen this function runs:

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary  *)info {
NSURL *mediaUrl;

mediaUrl = (NSURL *)[info valueForKey:UIImagePickerControllerMediaURL];

if (mediaUrl == nil)
{
    imagePuzzle = (UIImage *) [info valueForKey:UIImagePickerControllerEditedImage];
    if(imagePuzzle == nil)
    {
        //--- Original Image was selected ---
        imagePuzzle = (UIImage *) [info valueForKey:UIImagePickerControllerOriginalImage];
    }
    else {
        //--- Get the edited image ---
        //--- If it was successful the above valueForKey:UIImagePickerControllerEditedImage
        //--- would have assigned it already.
    }
}
else {
    //--- Muppet selected a video
}

// Animate the picker window going away
[picker dismissModalViewControllerAnimated:YES];
ImageViewController *imageViewController = [[ImageViewController alloc] init];
imageViewController.delegate = self;    
[self presentModalViewController:imageViewController animated:YES];
[imageViewController release];
}

This is where my problem lies. I've tried many different hacks and iterations but the above code is the simplest to present the problem. When the imageViewController is displayed as a modal dialogue the following exception is thrown:

2010-07-09 15:29:29.667 Golovomka[15183:207] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'Attempting to begin a modal
transition from <NewViewController: 0x5915f80> to <ImageViewController: 0x594a350>     
while a transition is already in progress. Wait for viewDidAppear/viewDidDisappear
to know the current transition has completed'

How do I cure this? I have tried delays and other tricks but do not really understand how I'm supposed to use viewDidAppear or viewDidDisappear to help me. Also of note is that a very basic application with one view loading the picker then displaying another view with the image in does not produce the error. Any help gratefully received.

Diziet
  • 2,397
  • 1
  • 26
  • 34

4 Answers4

9

To address the specific issue described here, you could add the viewDidAppear method in your class:

-(void)viewDidAppear:(BOOL)animated
{
    if (/*just visited ImagePicker*/)
    {
        ImageViewController *imageViewController = [[ImageViewController alloc] init];
        imageViewController.delegate = self;    
        [self presentModalViewController:imageViewController animated:YES];
        [imageViewController release];
    }
}

Remove those lines from below your call:

[picker dismissModalViewControllerAnimated:YES];

So, whenever your class self appears (is displayed), it will call viewDidAppear... Since this most likely isn't really what you want all the time, you could add some variables to set/clear that defines whether or not to immediately present the imageViewController when self is displayed. Something like "If coming from image picker, show the imageViewController, otherwise do nothing".

That said, imho, pushing modal views is should generally be done in response to a user action and I would maybe rethink the user experience here - e.g. add a subview instead of pushing a modal view which you could do where your currently have the code - but if you're just playing around with some tutorials that should solve the NSInternalInconsistencyException. :) Cheers!

Eric
  • 3,865
  • 4
  • 32
  • 44
  • Thanks for the advice, I'll try that in my code as it's neater than what I am doing. I'm pushing the modal view after the user has taken a picture or selected a photo. The idea here is that there is some potentially intense image processing and I want to display that modally to the user until it completes. So there is user action involved before forcing the modal view. As an aside I eventually killed the exception with this:
    [picker dismissModalViewControllerAnimated:YES]; [picker.view.superview removeFromSuperview];
    – Diziet Jul 10 '10 at 11:51
  • OK picker.view.superview removeFromSuperview. Really really bad solution unless you like blank screens for everything afterwards! I rebuilt the app from scratch in the end using a nav bar controller within a tab bar app and that worked fine. On another note, your solution also worked. Thank you. Much much clearer now. I've only been doing this for 2 days from a C/C++/Java/Perl/bash background. – Diziet Jul 10 '10 at 18:50
5

In iOS 5.0 and above you can use

[self dismissViewControllerAnimated:YES completion:^{
  //present another modal view controller here
}];
Luong Huy Duc
  • 458
  • 7
  • 17
2

I ran into this issue quite a few times. I recently started using this simple fix:

When I am going to present a new modal view controller immediately after dismissing another modal view controller, I simply dismiss the first one with argument NO in dismissModalViewControllerAnimated:.

Since the second view is presented with an animation, you hardly notice that the first one goes away fast. And you never get the transitions conflict.

dasdom
  • 13,975
  • 2
  • 47
  • 58
Dirk Puis
  • 21
  • 1
0

I was having the same problem when i wanted to present an MFMailComposeViewController immediately after dismissing the UIImagePickerController. Heres what i did:

  1. I removed the [imagePicker release]; statement from where i was presenting the image picker and put it in didFinishPickingMedia callback.

  2. I used [self performSelector:@selector(presentMailComposer:) withObject:image afterDelay:1.0f];

Here's my code: Displaying Image Picker

if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { NSArray *media = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];

    if ([media containsObject:(NSString*)kUTTypeImage] == YES) {
        UIImagePickerController *picker = [[UIImagePickerController alloc] init];
        [picker setMediaTypes:[NSArray arrayWithObject:(NSString *)kUTTypeImage]];
        picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        picker.delegate = self;
        [self presentModalViewController:picker animated:YES];
        //[picker release];
    }

}
else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unavailable!"
                                                    message:@"Could not open the Photo Library."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
}

Image Picker Delegate Callback - didFinishPickingMedia

NSString *mediaType = [info valueForKey:UIImagePickerControllerMediaType];

if([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
    UIImage *photoTaken = [info objectForKey:@"UIImagePickerControllerOriginalImage"];

    //Save Photo to library only if it wasnt already saved i.e. its just been taken
    if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
        UIImageWriteToSavedPhotosAlbum(photoTaken, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    }

    //Pull up MFMailComposeView Controller
    [self performSelector:@selector(composeMailWithPhoto:) withObject:photoTaken afterDelay:1.0f];
}

[picker dismissModalViewControllerAnimated:YES];
[picker release];

Display Mail Composer View

if ([MFMailComposeViewController canSendMail]) {

    MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
    mailPicker.mailComposeDelegate = self;

    // Fill out the email fields and Attach photograph to mail
    static NSString *imageType = @"image/jpeg";
    NSString *imageName = [NSString stringWithString:@"MyCoffeeCup.jpg"];
    NSData *imageData = UIImageJPEGRepresentation(image, 1.0);

    [mailPicker addAttachmentData:imageData mimeType:imageType fileName:imageName];
    [mailPicker setToRecipients:[NSArray arrayWithObject:@"hello@xische.com"]];

    [self presentModalViewController:mailPicker animated:YES];
    //[self.navigationController pushViewController:mailPicker animated:YES];
    [mailPicker release];
}
else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unavailable!"
                                                    message:@"This device cannot send emails."
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
}

Bushra Shahid
  • 3,579
  • 1
  • 27
  • 37