Ios 如何在UIImageView中异步加载图像?
我有一个UIView和一个UIImageView子视图。我需要在不阻塞UI的情况下在UIImageView中加载图像。阻塞调用似乎是:Ios 如何在UIImageView中异步加载图像?,ios,objective-c,uiimageview,grand-central-dispatch,Ios,Objective C,Uiimageview,Grand Central Dispatch,我有一个UIView和一个UIImageView子视图。我需要在不阻塞UI的情况下在UIImageView中加载图像。阻塞调用似乎是:UIImage imageNamed:。以下是我认为解决这个问题的方法: -(void)updateImageViewContent { dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ UIImage *
UIImage imageNamed:
。以下是我认为解决这个问题的方法:
-(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)
但是,在加载图像时,UI仍然被阻止。我错过了什么
下面是一个展示这种行为的小代码示例:
基于UIImageView创建一个新类,将其用户交互设置为YES,在UIView中添加两个实例,并实现其touchesBegind方法,如下所示:
-(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
从加载图像的视图开始,几乎同时点击两个视图时,会发生什么情况?UI是否因为正在等待[self-setImage:[UIImage-imagename:@“woodenTile.jpg]”而被阻止代码>返回?如果是这样,我如何异步执行此操作
下面是一个ipmcc代码的示例
使用长按,然后拖动,围绕黑色正方形绘制一个矩形。正如我理解他的回答,理论上白色选择矩形不应该在第一次加载图像时被阻止,但实际上是这样
项目中包含两个图像(一个小的:woodenTile.jpg,一个大的:bois.jpg)。两者的结果是相同的
图像格式
我真的不明白这与我第一次加载图像时UI被阻止的问题有什么关系,但是PNG图像解码时不会阻止UI,而JPG图像会阻止UI
事件年表
UI的阻塞从这里开始
。。到此为止
AFN网络解决方案
大多数时候,它会记录:success:{512,512}
它还偶尔记录:success:{0,0}
有时:失败:{URL:file:///var/mobile/Appl...
但是图像永远不会改变。这是一个好方法:
-(void)updateImageViewContent {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage * img = [UIImage imageNamed:@"background.jpg"];
[[self imageView] setImage:img];
});
}
为什么不像这样使用第三方库呢?使用它,您只需声明AsyncImageView对象并传递要加载的url或图像。图像加载过程中会显示一个活动指示器,没有任何东西会阻止UI。问题是,UIImage
直到他是第一次实际使用/绘制。要强制在背景线程上执行此工作,您必须在执行主线程之前在背景线程上使用/绘制图像。-setImage:
。这对我有效:
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];
});
});
编辑:UI没有阻塞。您专门将其设置为使用UILongPressGestureRecognitizer
,默认情况下,该识别器在执行任何操作前等待半秒。主线程仍在处理事件,但在GR超时之前不会发生任何事情。如果执行此操作:
longpress.minimumPressDuration = 0.01;
…您会注意到它变得更加快速。图像加载不是问题所在
编辑2:我已经看过了发布到github的代码,它在iPad 2上运行,我根本不明白你所描述的问题。事实上,它非常流畅。下面是在CoreAnimation instrument中运行代码的截图:
正如你在上图中看到的,FPS一直上升到~60FPS,并在整个手势过程中保持不变。在下图中,你可以看到16秒左右的光点,这是图像第一次加载的位置,但你可以看到帧速率没有下降。仅从目测中,我看到选择层相交,并且有一个小的变化l、 但是在第一个交叉点和图像出现之间有明显的延迟。据我所知,背景加载代码正在按预期工作
我希望我能为您提供更多帮助,但我看不出问题所在。您可以使用库,在库中导入类别
“UIImageView+AFNetworking.m”
并使用以下方法:
[YourImageView setImageWithURL:[NSURL URLWithString:@"http://image_to_download_from_serrver.jpg"]
placeholderImage:[UIImage imageNamed:@"static_local_image.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
//ON success perform
}
failure:NULL];
希望这能有所帮助。我的应用程序也遇到了一个非常类似的问题,我不得不下载大量图像,同时我的UI也在不断更新。下面是解决我问题的简单教程链接:
考虑使用:它不仅在后台下载和缓存图像,还加载和渲染图像
我在tableview中使用过它,效果很好,它的大图像即使在下载后也很难加载。
这是一个可以处理背景加载的图像视图
用法
要获取文件的URL,您可能会发现以下有趣的内容:
在应用程序中使用AFNetwork时,不需要使用任何块来加载映像,因为AFNetwork为其提供了解决方案。如下所示:
#import "UIImageView+AFNetworking.h"
及
谢谢我实现它的一个方法是:(尽管我不知道它是否是最好的)
首先,我使用串行异步或并行队列创建队列
queue = dispatch_queue_create("com.myapp.imageProcessingQueue", DISPATCH_QUEUE_SERIAL);**
or
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
**
你可能会发现它更适合你的需要
之后:
dispatch_async( queue, ^{
// Load UImage from URL
// by using ImageWithContentsOfUrl or
UIImage *imagename = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
// Then to set the image it must be done on the main thread
dispatch_sync( dispatch_get_main_queue(), ^{
[page_cover setImage: imagename];
imagename = nil;
});
});
-(void)touchsbegind:在主线程中调用。通过调用dispatch_async(dispatch_get_main_queue),您只需将块放入队列中。当队列准备就绪时(即系统已结束处理您的触摸),GCD将处理此块。这就是为什么在释放手指并让GCD处理已在主队列中排队的所有块之前,您无法看到woodenTile加载并分配给self.image的原因
替换:
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
});
作者:
应该可以解决您的问题…至少对于展示它的代码来说是这样。您没有遗漏任何东西。UI更改必须始终同步。这是您能做的最好的事情。图像有多大?您可能会发现,阻止调用不是加载图像,而是在屏幕上显示图像视图时呈现图像内容。H你有没有试过对应用程序进行分析,看看瓶颈在哪里?你只是盲目地假设吗
queue = dispatch_queue_create("com.myapp.imageProcessingQueue", DISPATCH_QUEUE_SERIAL);**
or
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
dispatch_async( queue, ^{
// Load UImage from URL
// by using ImageWithContentsOfUrl or
UIImage *imagename = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
// Then to set the image it must be done on the main thread
dispatch_sync( dispatch_get_main_queue(), ^{
[page_cover setImage: imagename];
imagename = nil;
});
});
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
});
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];