【问题标题】:Setting image property of UIImageView causes major lag设置 UIImageView 的图像属性会导致严重滞后
【发布时间】:2012-06-03 03:32:21
【问题描述】:

让我告诉您我遇到的问题以及我如何尝试解决它。我有一个 UIScrollView 加载子视图作为一个从左到右滚动。每个子视图有 10-20 个图像,每个图像大约 400x200。当我从一个视图滚动到另一个视图时,我会遇到相当多的延迟。

经过调查,我发现卸载所有视图后再次尝试,延迟消失了。我认为图像的同步缓存是滞后的原因。所以我创建了一个异步加载图像的 UIImageView 的子类。加载代码如下所示(self.dispatchQueue 返回一个串行调度队列)。

- (void)loadImageNamed:(NSString *)name {
    dispatch_async(self.dispatchQueue, ^{
        UIImage *image = [UIImage imageNamed:name];

        dispatch_sync(dispatch_get_main_queue(), ^{
            self.image = image;
        });
    });
}

但是,在将我所有的 UIImageViews 更改为这个子类之后,我仍然遇到了延迟(我不确定它是否减少了)。我将问题的原因归结为self.image = image;。为什么这会导致如此多的延迟(但仅在第一次加载时)?

请帮助我。 =(

【问题讨论】:

    标签: performance asynchronous uiimageview uiimage core-graphics


    【解决方案1】:

    编辑 3:iOS 15 现在提供 UIImage.prepareForDisplay(completionHandler:)

    image.prepareForDisplay { decodedImage in
        imageView.image = decodedImage
    }
    

    imageView.image = await image.byPreparingForDisplay()
    

    编辑 2:这是一个包含一些改进的 Swift 版本。 (未经测试。) https://gist.github.com/fumoboy007/d869e66ad0466a9c246d


    编辑:实际上,我相信所有必要的就是以下内容。 (未经测试。)

    - (void)loadImageNamed:(NSString *)name {
        dispatch_async(self.dispatchQueue, ^{
            // Determine path to image depending on scale of device's screen,
            // fallback to 1x if 2x is not available
            NSString *pathTo1xImage = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];
            NSString *pathTo2xImage = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"png"];
    
            NSString *pathToImage = ([UIScreen mainScreen].scale == 1 || !pathTo2xImage) ? pathTo1xImage : pathTo2xImage;
    
    
            UIImage *image = [[UIImage alloc] initWithContentsOfFile:pathToImage];
    
            // Decompress image
            if (image) {
                UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    
                [image drawAtPoint:CGPointZero];
    
                image = UIGraphicsGetImageFromCurrentImageContext();
    
                UIGraphicsEndImageContext();
            }
    
    
            // Configure the UI with pre-decompressed UIImage
            dispatch_async(dispatch_get_main_queue(), ^{
                self.image = image;
            });
        });
    }
    

    原始答案:事实证明,它不是直接self.image = image;。 UIImage 图像加载方法不会立即解压缩和处理图像数据;当视图刷新其显示时,他们会这样做。所以解决的办法是降级到Core Graphics,自己解压处理图像数据。新代码如下所示。

    - (void)loadImageNamed:(NSString *)name {
        dispatch_async(self.dispatchQueue, ^{
            // Determine path to image depending on scale of device's screen,
            // fallback to 1x if 2x is not available
            NSString *pathTo1xImage = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];
            NSString *pathTo2xImage = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"png"];
            
            NSString *pathToImage = ([UIScreen mainScreen].scale == 1 || !pathTo2xImage) ? pathTo1xImage : pathTo2xImage;
            
            
            UIImage *uiImage = nil;
            
            if (pathToImage) {
                // Load the image
                CGDataProviderRef imageDataProvider = CGDataProviderCreateWithFilename([pathToImage fileSystemRepresentation]);
                CGImageRef image = CGImageCreateWithPNGDataProvider(imageDataProvider, NULL, NO, kCGRenderingIntentDefault);
                
                
                // Create a bitmap context from the image's specifications
                // (Note: We need to specify kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
                // because PNGs are optimized by Xcode this way.)
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                CGContextRef bitmapContext = CGBitmapContextCreate(NULL, CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetWidth(image) * 4, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
                
                
                // Draw the image into the bitmap context
                CGContextDrawImage(bitmapContext, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
                
                //  Extract the decompressed image
                CGImageRef decompressedImage = CGBitmapContextCreateImage(bitmapContext);
                
                
                // Create a UIImage
                uiImage = [[UIImage alloc] initWithCGImage:decompressedImage];
                
                
                // Release everything
                CGImageRelease(decompressedImage);
                CGContextRelease(bitmapContext);
                CGColorSpaceRelease(colorSpace);
                CGImageRelease(image);
                CGDataProviderRelease(imageDataProvider);
            }
            
            
            // Configure the UI with pre-decompressed UIImage
            dispatch_async(dispatch_get_main_queue(), ^{
                self.image = uiImage;
            });
        });
    }
    

    【讨论】:

    • 哎呀...对不起那个小错误。
    • 这很有帮助。对于那些只需要从 UIView 制作 UIImage 的人来说,我在拼凑许多答案后找到了这个解决方案:UIGraphicsBeginImageContext(processing.bounds.size); CGContextRef ctx = UIGraphicsGetCurrentContext(); ` [processing.layer renderInContext:ctx]; ` UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();`
    • 谢谢!非常有帮助的答案!
    • 惊人的答案,在 2015 年仍然很有帮助:)
    【解决方案2】:

    我认为,问题可能出在图像本身。例如 - 我在我的一个项目中获得了 10 张 640x600 的图像,彼此之间具有 alpha 透明度。当我尝试从这个视图控制器中推送或弹出视图控制器时......它滞后了很多。

    当我只留下几张图片或使用非常小的图片时 - 没有延迟。

    附:在 sdk 4.2 ios5 iphone 4 上测试。

    【讨论】:

    • 但我的只在第一次加载时滞后!
    猜你喜欢
    • 2022-07-29
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-28
    • 2019-12-25
    • 1970-01-01
    相关资源
    最近更新 更多