Long delay when saving a picture taken with the camera or selected from a camera roll - iPhone

I use the following code to allow the user of my application to take / select a photo, which will then be saved in the document directory and installed as a UIImageView image:

-(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 is very slow, for example, when I select an image from the camera roll, it ultimately saves and returns me to the calling mode, but only after a long delay ..

Can anyone shed some light on this?

+4
source share
2 answers

Saving large images (such as those taken with the camera on the iPhone 4 / 4S) is time consuming. If you profile the process, you will find that UIImagePNGRepresentation() takes some time to generate your PNG image, but the main bottleneck in my experience was writing to a disk with a 1+ MB image.

Not only can you do to speed up this process, in addition to using JPEG compression, which I found in my tests a little faster, or using a faster third-party image compression procedure. Therefore, if you do not want to block your user interface while this happens, send this save process to a background thread or queue. You can 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, be aware that each of these UIImages uses quite a bit of memory for storage, so you can use the send semaphore to prevent several such image-saving operations from being performed simultaneously.

Just like a stylistic note, an Objective-C method definition of type

 - (void)saveImage:(UIImage*)image:(NSString*)imageName 

while allowed, very discouraged. Give each parameter a name, for example, the following:

 - (void)saveImage:(UIImage*)image fileName:(NSString*)imageName 

This will make your code more understandable.

+8
source

I answered a similar question . For clarity, I will copy it here:

Depending on the resolution of the image, UIImagePNGRepresentation can be quite slow, like any write to the file system.

You should always perform these types of operations in an asynchronous queue. Even if the performance seems good enough for your application when testing, you should still make it an asynchronous queue - you never know what other processes can occur on the device, which can slow down saving after your application is in the hands of users.

Newer versions of iOS make it possible to save asynchronously, very simply, using Grand Central Dispatch (GCD). Steps:

  • Create an NSBlockOperation that Saves the Image
  • In the block for completing the operation of the block, read the image from the disk and display it. The only caveat is that you should use the main queue to display the image: all user interface operations must be performed in the main thread.
  • Add a block operation to the operation queue and see how it happens!

What is it. And here is 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 like this code template, you can use it. This is incredibly useful when creating large data sets, lengthy load operations, etc. In fact, any operation that makes your user interface concise is at least a good candidate for this code. Just remember, you can’t do anything with the interface until you’re in the main line and the rest is cake.

+2
source

Source: https://habr.com/ru/post/1416381/


All Articles