In my iPhone app, I am using the iPhone\'s camera to take a photo and save it do disk (the application\'s documents folder). This is how i save it:
[UIImageJ
This is the faster way I know. You'll need to import #import <ImageIO/ImageIO.h>
I use this code to download and compress images during a scroll, inside a scrollview and you barely notice the delay.
CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)mutableData, NULL);
CFDictionaryRef options = (CFDictionaryRef)[[NSDictionary alloc] initWithObjectsAndKeys:(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform, (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent, (id)[NSNumber numberWithDouble:200.0], (id)kCGImageSourceThumbnailMaxPixelSize, nil];
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
UIImage *image = [[UIImage alloc] initWithCGImage:thumbnail];
// Cache
NSString *fileName = @"fileName.jpg";
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"thumbnail"];
path = [path stringByAppendingPathComponent:fileName];
if ([UIImagePNGRepresentation(image) writeToFile:path atomically:YES]) {
// Success
}
I face a very similar issue where I had to load hundreds of images from the directory. My performance was quite slow if I used UIImage(contentsOfFile:) method. The below method increased my performance to 70 %.
class ImageThumbnailGenerator: ThumbnailGenerator { private let url: URL
init(url: URL) {
self.url = url
}
func generate(size: CGSize) -> UIImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil) else {
return nil
}
let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: Double(max(size.width, size.height) * UIScreen.main.scale),
kCGImageSourceCreateThumbnailFromImageIfAbsent: true
]
return CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as NSDictionary).flatMap { UIImage(cgImage: $0) }
}
}
+imageWithContentsOfFile:
is synchronous, so the UI on your main thread is being blocked by the image loading from disk operation and causing the choppiness. The solution is to use a method that loads the file asynchronously from disk. You could also do this in a background thread. This can be done easily by wrapping the +imageWithContentsOfFile:
in dispatch_async()
, then a nested dispatch_async()
on the main queue that wraps -setBackgroundImage:
since UIKit methods need to be run on the main thread. If you want the image to appear immediately after the view loads, you'll need to pre-cache the image from disk so it's in-memory immediately when the view appears.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:frontPath];
dispatch_async(dispatch_get_main_queue(), ^{
[self.frontButton setBackgroundImage:image forState:UIControlStateNormal];
});
});
As an aside, if the button image happens a gradient, consider using the following properties to ensure the image file loaded from disk is tiny:
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets
or (deprecated, only use if you need to support iOS 4.x):
- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight