4

I have a UITableViewController that pops up a UIImagePickerController, user takes a pic, hits the Use button, Picker dismisses to show a custom thumbnail spinner while the image is being processed, then the spinner is replaced by the actual thumbnail at the end of processing.

At least that's how it worked in iOS4. Now with iOS5 it just sits there processing until it's finished, and then everything works correctly. But I want that spinner in there so the user knows something's happening, otherwise it looks like it just hung.

So I have this:

- (void) actionSheet: (UIActionSheet *)actionSheet didDismissWithButtonIndex (NSInteger)buttonIndex {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = NO;
    // yada, yada, yada
    [self presentModalViewController:picker animated:YES];
    [picker release];
}

And then this gets called when the user picks "Use":

- (void) imagePickerController: (UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [self dismissModalViewControllerAnimated:YES];
    [self performSelectorOnMainThread:@selector(processImage:) withObject:info waitUntilDone:NO];
    animate = true;
}

And then this gets called to perform the processing while the thumbnail is spinning:

- (void) processImage:(NSDictionary *)info
{
    UIImage *image = nil;
    NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
    // processing of image
    animate = false;
    [activityImageView stopAnimating];
    [activityImageView release];
    [self.tableView reloadData];
}

Like I said, it worked perfectly with iOS4, but with iOS5, no such luck. So what's the deal? The image picker gets dismissed eventually, so why doesn't it get dismissed immediately?

mutatron
  • 563
  • 3
  • 7
  • 19
  • I truly don't know why it should matter, but have you tried replacing `[self performSelector...` with `dispatch_async(dispatch_get_main_queue(), ^{[self processImage:info];});`? – NJones Dec 07 '11 at 03:31
  • Good thought, but same behavior. At least I learned something new though! – mutatron Dec 07 '11 at 05:51

2 Answers2

4

I'm not sure why there's a disparity between iOS4 & iOS5 on this point. But your description of the UI hanging is fairly consistent with the code you've shown. The perform selector on main thread is doing just that, performing the selector on the main thread, which is the thread you're calling from. Because of this setting waitUntilDone: to NO is meaningless since it's not being sent to another thread it's simply running in order. You would probably get the results you want just from swapping the order, like So:

[self dismissModalViewControllerAnimated:YES];
animate = true;
[self performSelectorOnMainThread:@selector(processImage:) withObject:info waitUntilDone:NO];

But please note that this would be risky at best, since I assume // processing of image contains no concurrency. I prefer blocks for concurrency. And on top of that I like nested blocks to make the concurrency easy to follow, for example:

-(void)doSomeStuffInBackground{
    // Prepare for background stuff
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // Do background stuff
        dispatch_async(dispatch_get_main_queue(), ^{
            // Update UI from results of background stuff
        });
    });
}

So with that in mind, I would suggest something more like this:

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    [self dismissModalViewControllerAnimated:YES];
    [self processImage:info];
}

-(void)processImage:(NSDictionary *)info{
    animate = true;
    UIImage *image = nil;
    NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // processing of image here on background thread

        dispatch_async(dispatch_get_main_queue(), ^{
            // update UI here on main thread
            animate = false;
            [activityImageView stopAnimating];
            [activityImageView release];
            [self.tableView reloadData];
        });
    });
}

This would offload the main work to a background thread to let the UI stay responsive.

NJones
  • 27,139
  • 8
  • 70
  • 88
  • Perfect! That does exactly what I want, and I learned something too. Actually, I now remember having learned something like that in a class I took, but I guess it didn't stick with me. Thanks! – mutatron Dec 08 '11 at 05:55
0

Try to use

[[picker presentingViewController] dismissViewControllerAnimated:YES completion:nil]; 

instead of:

[[picker parentViewController] dismissModalViewControllerAnimated: YES];
Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
Gigi
  • 616
  • 6
  • 7
  • Welcome to Stack Overflow! Thanks for your post! Please do not use signatures/taglines in your posts. Your user box counts as your signature, and you can use your profile to post any information about yourself you like. [FAQ on signatures/taglines](http://stackoverflow.com/faq#signatures) – Andrew Barber Feb 24 '13 at 09:39