如何在UIImageView中异步加载图像? [英] How to asynchronously load an image in an UIImageView?

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

问题描述

我有一个带有UIImageView子视图的UIView。我需要在UIImageView中加载图像而不阻止UI。阻塞调用似乎是: 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 ?

这是使用ipmcc代码在github上投影

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

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.


阻止用户界面从这里开始..

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

大部分时间,它记录:成功:{512,512 }

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

它还偶尔记录:成功:{0,0}

有时:失败:< NSURLResponse:0x146c0000> {URL: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并在整个手势中保持不变。在底部图表中,您可以看到大约16s处的光点,这是图像首次加载的位置,但您可以看到帧速率没有下降。仅从视觉检查,我看到选择层相交,并且在第一个交叉点和图像外观之间存在一个小但可观察到的延迟。据我所知,后台加载代码正在按预期工作。

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中异步加载图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆