I am using the following code to allow a user of my application to take/choose a photo, which will then be saved to the documents directory and set as the image of a UIImageView:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (actionSheet.tag == 0){
if (buttonIndex == 0) {
NSLog(@"Take Picture Button Clicked");
// Create image picker controller
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// Set source to the camera
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
// Delegate is self
imagePicker.delegate = self;
// Show image picker
[self presentModalViewController:imagePicker animated:YES];
}
else if (buttonIndex == 1) {
NSLog(@"Choose From Library Button Clicked");
// Create image picker controller
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// Set source to the camera
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// Delegate is self
imagePicker.delegate = self;
// Show image picker
[self presentModalViewController:imagePicker animated:YES];
}
else if (buttonIndex == 2) {
NSLog(@"Cancel Button Clicked");
}
}
......
- (void)saveImage:(UIImage*)image:(NSString*)imageName
{
NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
NSFileManager *fileManager = [NSFileManager defaultManager];//create instance of NSFileManager
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path
[fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)
receiptImageView1.image = [UIImage imageWithContentsOfFile:fullPath];
self.receiptImage1 = fullPath;
NSLog(@"image saved");
}
//Receive the image the user picks from the image picker controller
-(void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info {
UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage];
NSString* imageName = @"Receipt1Image1";
[self saveImage:image :imageName];
}
Basically my problem is that this code seems to be executing very slowly, for example when I select an image from the camera roll it does eventually save and bring me back to the calling view, but only after a long delay..
Can anyone shed any light on this?
Saving large images (like the ones taken by the camera in an iPhone 4/4S) takes a long time. If you profile the process, you'll find that UIImagePNGRepresentation()
takes a while to generate your PNG image, but the primary bottleneck in my experience has been the writing to disk of a 1+ MB image.
There's little you can do to speed up this process aside from using JPEG compression, which I've found to be a little faster in my benchmarks, or using a faster third-party image compression routine. Therefore, if you don't want to block your user interface while this is happening, dispatch this saving process on a background thread or queue. You could do something like the following:
-(void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage];
NSString* imageName = @"Receipt1Image1";
[self saveImage:image :imageName];
});
}
However, watch out for the fact that each of these UIImages use up quite a bit of memory to keep around, so you may want to use a dispatch semaphore to prevent more than one such image saving operation from occurring at the same time.
Also, as a stylistic note, defining an Objective-C method like
- (void)saveImage:(UIImage*)image:(NSString*)imageName
while allowed, is highly discouraged. Give each parameter a name, like in the following:
- (void)saveImage:(UIImage*)image fileName:(NSString*)imageName
It will make your code a lot more descriptive.
I've answered a similar question. For clarity I will copy it here:
Depending on the image resolution, UIImagePNGRepresentation
can indeed be quite slow, as can any writing to the file system.
You should always execute these types of operations in an asynchronous queue. Even if the performance seems good enough for your application when testing, you should still do it an asynch queue -- you never know what other processes the device might have going on which might slow the save down once your app is in the hands of users.
Newer versions of iOS make saving asynchronously really, really easy using Grand Central Dispatch (GCD). The steps are:
NSBlockOperation
which saves the imageThat's it. And here's the code:
// Create a block operation with our saves
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^{
[UIImagePNGRepresentation(image) writeToFile:file atomically:YES];
[UIImagePNGRepresentation(thumbImage) writeToFile:thumbfile atomically:YES];
}];
// Use the completion block to update our UI from the main queue
[saveOp setCompletionBlock:^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
UIImage *image = [UIImage imageWithContentsOfFile:thumbfile];
// TODO: Assign image to imageview
}];
}];
// Kick off the operation, sit back, and relax. Go answer some stackoverflow
// questions or something.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:saveOp];
Once you are comfortable with this code pattern, you will find yourself using it a lot. It's incredibly useful when generating large datasets, long operations on load, etc. Essentially, any operation that makes your UI laggy in the least is a good candidate for this code. Just remember, you can't do anything to the UI while you aren't in the main queue and everything else is cake.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With