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?
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.
Try to use
[[picker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
instead of:
[[picker parentViewController] dismissModalViewControllerAnimated: YES];
Related
iOS 7 using UIImagePickerController to take picture twice, at second time will show a static image covered the camera, how to reset the camera.
I'm try to take picture one by one, and keep take 5 pictures.
It works on iOS6.
on iOS7, it works fine at the first time, but when it take picture at second time, it will show a static dark image on screen, how to clear or reset it, although take picture works, but user can't see what will capture with camera.
bool fistTimeToShow = YES;
-(void) viewDidAppear:(BOOL)animated{
if (fistTimeToShow) {
fistTimeToShow = NO;
self.imagePickerController = nil;
[self tackImage];
}
}
-(void)tackImage{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.imagePickerController = [[UIImagePickerController alloc]init];
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imagePickerController.showsCameraControls = YES;
self.imagePickerController.delegate = self;
[self presentViewController:self.imagePickerController animated:NO completion:nil];
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(#"====== imagePickerController:didFinishPickingMediaWithInfo ======");
[self.imagePickerController dismissViewControllerAnimated:NO completion:nil];
//...deal with the image capture...
self.imagePickerController = nil;
[self tackImage];
}
Update
I change the dismiss function to put the [self tackImage]; in block. And now it always show the fist image took.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(#"====== imagePicker: didFinish ======");
self.imagePickerController = nil;
[self dismissViewControllerAnimated:NO completion: ^{
[self tackImage];
}];
}
I'm trying to find a way to clear the image. But I don't know where the image saved yet.
Update2
use
[self performSelector:#selector(presentCameraView) withObject:nil afterDelay:1.0f];
and function
-(void)presentCameraView{
[self presentViewController:self.imagePickerController animated:NO completion:nil];
}
to replace. [self presentViewController:self.imagePickerController animated:NO completion:nil]; it works on my device anyway, but I don't even know why.
Update3
I have set the userInteractionEnabled to NO when Delay:1.0f to avoid other problems, and may be also need set the navigationBar and tabBar for specifically use.
I had exactly the same issue under iOS 7 using Xamarin.iOS.
What has helped, is adding GC.Collect() in didFinishPickingMediaWithInfo method.
In C# GC.Collect() "cleans up" the memory from unused/disposed objects.
For sure, there's no direct equivalent for that in Obj-C, but that may shed some light on your problem also (it could be memory-related).
I have created a app which pick image from Camera and on didFinishPickingMediaWithInfo pass this to another view and load this view. I think code is okay but it takes appx 15-20 seconds to display second view. So please let me know how can I fix this loading issue.
My code block is :
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[NSThread detachNewThreadSelector:#selector(switchToSecondView:) toTarget:self withObject:info];
}
-(void) switchToSecondView:(NSDictionary *)info
{
UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];
secController =[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secController.myimage = image;
AppDelegate *appDelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.navController pushViewController:secController animated:NO];
}
Thanks,
Laxmilal Menaria
UiKit is [overwhelmingly] safe to use on the main thread only. Your work in switchToSecindView: is happening on a background thread. Behaviour is therefore undefined.
Likely what's happening is that UIKit is failing to notice your changes until it happens to be doing some other processing.
I'm using a UIImagePickerController to select an image from my photo album. Once I've selected the image, I'm passing the image through to a second view controller and displaying it in a UIImageView. See code below:
First view controller:
- (IBAction)selectPhoto
{
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
[self presentModalViewController:self.imagePicker animated:YES];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UploadViewController *uploadViewController = [[UploadViewController alloc] initWithNibName:#"UploadViewController" bundle:nil];
[uploadViewController setImage:[info objectForKey:#"UIImagePickerControllerOriginalImage"]];
[picker pushViewController:uploadViewController animated:YES];
}
Second view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the image view image
imageView.image = self.image;
}
The code does the job, however, when I push from the image picker to my second view controller, it lags as it's transitioning.
Ideally I would like a smooth transition but I would be happy if it just waited half a second or something and then moves smoothly.
Can any explain why this could be happening and how/if I can get around it?
Thanks.
The delay is probably from rendering the image, you could try having your UploadViewController's initial view contain an activity spinner then actually set the image in the viewDidAppear method, this method should be called after the animation is completed.
Try with this , here timer is introduced in order to provide delay(0.5sec) ,
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[NSTimerscheduledTimerWithTimeInterval:0.5 target:self selector:#selector(timerAction:) userInfo:info repeats:NO];
}
-(void)timerAction:(NSTimer *)timer
{
UploadViewController *uploadViewController = [[UploadViewController alloc] initWithNibName:#"UploadViewController" bundle:nil];
[uploadViewController setImage:[[timer userInfo]objectForKey:#"UIImagePickerControllerOriginalImage"]];
[picker pushViewController:uploadViewController animated:YES];
[uploadViewController release];
}
I have a simple iPhone application.
I display a UIImagePicker, and let the user select an image.
I then execute some code on the image, and want this code to execute after the UIImagePicker has been dismissed.
EDIT: updated code, problem remains:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSData* data = UIImagePNGRepresentation([info objectForKey:#"UIImagePickerControllerOriginalImage"]);
[[picker parentViewController] dismissModalViewControllerAnimated:TRUE];
[self executeSomeCode: data];
}
However, when I run the code, it hangs on the UIImagePicker to run my code, and then the UIImagePicker is visually dismissed after said code has executed.
Any ideas?
I realize this isn't the most graceful of answers. I've had similar problems before (trying to do visual things in viewDidAppear that wasn't working). I found that by calling performSelector:withObject:afterDelay: caused my visual effects to be seen.
So in your case it would be:
[self performSelector:#selector(executeSomeCode:) withObject:data afterDelay:0];
I have no idea if this will work for you (or why it worked for me).
It shouldn't be necessary to use a background method, but you can try this approach:
- (void)_processImage:(UIImage*)image
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// convert to PNG data here, etc.
// this is background, remember to manipulate UI in main thread
// Example: [self.anImageView performSelectorOnMainThread:#selector(setImage:) withObject:image waitUntilDone:NO];
[pool release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
UIImage *picked = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
[self performSelectorInBackground:#selector(_processImage:) withObject:[[picked retain] autorelease]];
}
http://developer.apple.com/iphone/library/documentation/uikit/reference/UIImagePickerControllerDelegate_Protocol/UIImagePickerControllerDelegate/UIImagePickerControllerDelegate.html
I believe your call was deprecated in iOS3
You might try
(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
and the get the data from the image before dismissing the viewController
NSData *data = UIImageJPEGRepresentation( [info objectForKey:#"UIImagePickerControllerOriginalImage"] , 1.0);
[self dismissModalViewControllerAnimated:TRUE];
[self executeSomeCode:img];
I don't think you are hanging where you think you are. UIImagePNGRepresentation takes quite some time to perform as far as I know (UIImageJPEGRepresentation is much faster, if you don't mind not being lossless). You could pass the dictionary into your method and obtain the NSData you need in there.
Your second problem is that your code is synchronous, no matter how soon you tell your controller to dismiss, it will wait your long code to run. To schedule your heavy code on the next run loop iteration and let the controller be dismissed, use a 0ms delay to perform it.
I would change your code to this:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[self performSelector:#selector(executeSomeCode:) withObject:info afterDelay:0.0f];
}
I am trying to invoke UIImagePickerController to select a movie on iPhone 3GS and when the movie is selected, i just dismiss it and present MyViewController modally with a configured delay of 1.0 seconds. What I notice is 10% of the times, presentModalViewController on MyViewController does nothing whereas it works 90% of the times. I want to understand why is this behavior and what is the remedy. Here is the sample code:
(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSURL *videoURL = nil;
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:#"public.movie"])
{
videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
}
picker.delegate = nil;
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[self performSelector:#selector(launchMyViewController:) withObject:nil
afterDelay:1.0];
}
-(void) launchMyViewController:(id) obj
{
MyViewController *myCtrl = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle] controller:self];
[self presentModalViewController:myCtrl animated:YES];
[myCtrl release];
NSLog(NSStringFromClass([self.modalViewController class]));
[path release];
}
I have put NSLog statement to print the self.modalViewController class name and what I notice is that 10% of the times when myCtrl is not fired modally, the self.modalViewController.class is UIImagePickerController. Otherwise, the self.modalViewController.class is MyViewController. I want to know why is the behavior so unpredictable and what is the workaround or other way to achieve the same thing I intend.
I'm going to guess that using the performSelector:afterDelay: call is the problem. Try calling your launch method immediately, instead of through the delay.