在iPhone应用程序中从磁盘加载图像很慢

时间:2011-11-04 22:04:23

标签: iphone objective-c ios

在我的iPhone应用程序中,我正在使用iPhone的相机拍摄照片并将其保存到磁盘(应用程序的文档文件夹)。这是我如何保存它:

[UIImageJPEGRepresentation(photoTaken, 0.0) writeToFile:jpegPath atomically:YES];

使用最多压缩,我认为从磁盘读取图像会很快。但它不!我在我的一个视图中使用图像作为按钮的背景图像。我像这样加载它:

[self.frontButton setBackgroundImage:[UIImage imageWithContentsOfFile:frontPath] forState:UIControlStateNormal];

当我使用此按钮导航到视图时,它很慢且不连贯。我该如何解决这个问题?

3 个答案:

答案 0 :(得分:39)

+imageWithContentsOfFile:是同步的,因此主线程上的UI被磁盘操作中的图像加载阻塞并导致不稳定。解决方案是使用从磁盘异步加载文件的方法。您也可以在后台线程中执行此操作。这可以通过将+imageWithContentsOfFile:包装在dispatch_async()中,然后在包裹dispatch_async()的主队列上包装-setBackgroundImage:来轻松完成,因为UIKit方法需要在主线程上运行。如果您希望在加载视图后立即显示图像,则需要从磁盘预先缓存图像,以便在视图出现时立即在内存中。

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];
    });

});

另外,如果按钮图像出现渐变,请考虑使用以下属性来确保从磁盘加载的图像文件很小:

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets

或(不推荐使用,仅在您需要支持iOS 4.x时使用):

- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight

答案 1 :(得分:4)

这是我所知道的更快的方式。您需要导入#import <ImageIO/ImageIO.h>

我在滚动期间使用此代码下载和压缩图像,在滚动视图中,您几乎没有注意到延迟。

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
}

答案 2 :(得分:1)

我面临一个非常类似的问题,我必须从目录中加载数百个图像。如果我使用UIImage(contentsOfFile :)方法,我的表现相当慢。以下方法将我的表现提高到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) }
 }
}