如何在 UIImageView 中异步加载图像?

How to asynchronously load an image in an UIImageView?(如何在 UIImageView 中异步加载图像?)

本文介绍了如何在 UIImageView 中异步加载图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有 UIImageView 子视图的 UIView.我需要在不阻塞 UI 的情况下在 UIImageView 中加载图像.阻塞调用似乎是:UIImage imageNamed:.这是我认为解决此问题的方法:

I have an UIView with an UIImageView subview. I need to load an image in the UIImageView without blocking the UI. The blocking call seems to be: UIImage imageNamed:. Here is what I thought solved this problem:

-(void)updateImageViewContent {
    dispatch_async(
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        UIImage * img = [UIImage imageNamed:@"background.jpg"];
        dispatch_sync(dispatch_get_main_queue(), ^{
            [[self imageView] setImage:img];
        });
    });
}

图片很小 (150x100).

The image is small (150x100).

但是,加载图像时 UI 仍然被阻止.我错过了什么?

However the UI is still blocked when loading the image. What am I missing ?

这是一个展示这种行为的小代码示例:

Here is a small code sample that exhibits this behaviour:

基于 UIImageView 创建一个新类,将其用户交互设置为 YES,在一个 UIView 中添加两个实例,并实现其 touchesBegan 方法,如下所示:

Create a new class based on UIImageView, set its user interaction to YES, add two instances in a UIView, and implement its touchesBegan method like this:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.tag == 1) {
        self.backgroundColor= [UIColor redColor];
    }

    else {
    dispatch_async(dispatch_get_main_queue(), ^{
    [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
  });
    [UIView animateWithDuration:0.25 animations:
        ^(){[self setFrame:CGRectInset(self.frame, 50, 50)];}];
    }
}

将标签 1 分配给这些 imageView 之一.

Assign the tag 1 to one of these imageViews.

当您几乎同时点击两个视图(从加载图像的视图开始)时,究竟会发生什么?UI 是否因为等待 [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; 返回而被阻止?如果是这样,我该如何异步执行此操作?

What happens exactly when you tap the two views almost simultaneously, starting with the view that loads an image? Does the UI get blocked because it's waiting for [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; to return ? If so, how may I do this asynchronously ?

这是一个 github 上的项目,带有 ipmcc 代码

Here is a project on github with ipmcc code

使用长按然后拖动以在黑色方块周围绘制一个矩形.据我了解他的回答,理论上白色选择矩形不应该在第一次加载图像时被阻止,但实际上是.

Use a long press then drag to draw a rectangle around the black squares. As I understand his answer, in theory the white selection rectangle should not be blocked the first time the image is loaded, but it actually is.

项目中包含两张图片(一张小:woodenTile.jpg,一张大:bois.jpg).结果是一样的.

Two images are included in the project (one small: woodenTile.jpg and one larger: bois.jpg). The result is the same with both.

我真的不明白这与第一次加载图像时 UI 被阻塞的问题有什么关系,但是 PNG 图像在不阻塞 UI 的情况下解码,而 JPG 图像确实阻塞了 UI.

I don't really understand how this is related to the problem I still have with the UI being blocked while the image is loaded for the first time, but PNG images decode without blocking the UI, while JPG images do block the UI.

UI 的阻塞从这里开始..

The blocking of the UI begins here..

.. 到此结束.

    NSURL * url =  [ [NSBundle mainBundle]URLForResource:@"bois" withExtension:@"jpg"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    [self.imageView setImageWithURLRequest:request
                          placeholderImage:[UIImage imageNamed:@"placeholder.png"]
                                   success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
                                       NSLog(@"success: %@", NSStringFromCGSize([image size]));
                                   } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
                                       NSLog(@"failure: %@", response);
                                   }];

// this code works. Used to test that url is valid. But it's blocking the UI as expected.
if (false)       
if (url) {
        [self.imageView setImage: [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]]; }

大多数时候,它会记录:success: {512, 512}

Most of the time, it logs: success: {512, 512}

它偶尔也会记录:success: {0, 0}

有时:失败:<NSURLResponse: 0x146c0000>{ 网址:file:///var/mobile/Appl...

但图像永远不会改变.

推荐答案

问题是 UIImage 直到第一次实际使用/绘制图像时才真正读取和解码图像.要强制这项工作在后台线程上进行,您必须在执行主线程 -setImage: 之前在后台线程上使用/绘制图像.这对我有用:

The problem is that UIImage doesn't actually read and decode the image until the first time it's actually used/drawn. To force this work to happen on a background thread, you have to use/draw the image on the background thread before doing the main thread -setImage:. This worked for me:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    UIImage * img = [UIImage imageNamed:@"background.jpg"];

    // Make a trivial (1x1) graphics context, and draw the image into it
    UIGraphicsBeginImageContext(CGSizeMake(1,1));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), [img CGImage]);
    UIGraphicsEndImageContext();

    // Now the image will have been loaded and decoded and is ready to rock for the main thread
    dispatch_sync(dispatch_get_main_queue(), ^{
        [[self imageView] setImage: img];
    });
});

用户界面没有阻塞.您已将其专门设置为使用 UILongPressGestureRecognizer,默认情况下,它会在执行任何操作之前等待半秒.主线程仍在处理事件,但在 GR 超时之前什么都不会发生.如果你这样做:

The UI isn't blocking. You've specifically set it up to use UILongPressGestureRecognizer which waits, by default, a half a second before doing anything. The main thread is still processing events, but nothing is going to happen until that GR times out. If you do this:

    longpress.minimumPressDuration = 0.01;

...您会注意到它变得更加敏捷.图片加载不是这里的问题.

...you'll notice that it gets a lot snappier. The image loading is not the problem here.

编辑 2:我查看了发布到 github 上的代码,该代码在 iPad 2 上运行,但我根本不明白您所描述的问题.事实上,它非常顺利.这是在 CoreAnimation 工具中运行代码的屏幕截图:

EDIT 2: I've looked at the code, as posted to github, running on an iPad 2, and I simply do not get the hiccup you're describing. In fact, it's quite smooth. Here's a screenshot from running the code in the CoreAnimation instrument:

如上图所示,FPS 最高可达 ~60FPS,并在整个手势过程中保持不变.在底部的图表中,您可以在大约 16 秒处看到第一次加载图像的时间点,但您可以看到帧速率没有下降.仅通过目视检查,我看到选择层相交,并且在第一个相交与图像外观之间有一个小的但可观察到的延迟.据我所知,后台加载代码正在按预期工作.

As you can see on the top graph, the FPS goes right up to ~60FPS and stays there throughout the gesture. On the bottom graph, you can see the blip at about 16s which is where the image is first loaded, but you can see that there's not a drop in the frame rate. Just from visual inspection, I see the selection layer intersect, and there's a small, but observable delay between the first intersection and the appearance of the image. As far as I can tell, the background loading code is doing its job as expected.

我希望我能帮助你更多,但我只是没有看到问题.

I wish I could help you more, but I'm just not seeing the problem.

这篇关于如何在 UIImageView 中异步加载图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:如何在 UIImageView 中异步加载图像?