![](/img/trans.png)
[英]How to load image asynchronously using UIImageView+AFNetworking in ios
[英]How to asynchronously load an image in an UIImageView?
我有一个带有 UIImageView 子视图的 UIView。 我需要在不阻塞 UI 的情况下在 UIImageView 中加载图像。 阻塞调用似乎是: 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 中添加两个实例,并实现其 touchesBegan 方法,如下所示:
-(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 imageNamed:@"woodenTile.jpg"]];
返回 ? 如果是这样,我该如何异步执行此操作?
使用长按然后拖动以在黑色方块周围绘制一个矩形。 据我了解他的回答,理论上白色选择矩形不应该在第一次加载图像时被阻止,但实际上是这样。
项目中包含两张图片(一张小的:woodenTile.jpg,一张大的:bois.jpg)。 结果与两者相同。
我真的不明白这与我在第一次加载图像时仍然遇到的 UI 被阻塞的问题有什么关系,但是 PNG 图像在不阻塞 UI 的情况下解码,而 JPG 图像确实阻塞了 UI。
UI的阻塞从这里开始..
.. 到此结束。
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}
它还偶尔记录: success: {0, 0}
有时: failure: <NSURLResponse: 0x146c0000> { URL: file:///var/mobile/Appl...
但图像从未改变。
问题是UIImage
实际上并没有读取和解码图像,直到它第一次实际使用/绘制。 要在后台线程上强制执行此工作,您必须在执行主线程-setImage:
之前在后台线程上使用/绘制图像-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];
});
});
编辑:用户界面没有阻止。 您已经专门设置它以使用UILongPressGestureRecognizer
,默认情况下,在执行任何操作之前等待半秒钟。 主线程仍在处理事件,但在GR超时之前不会发生任何事情。 如果你这样做:
longpress.minimumPressDuration = 0.01;
......你会注意到它变得更加快捷。 这里的图像加载不是问题。
编辑2:我看过代码,发布到github,在iPad 2上运行,我根本没有得到你描述的打嗝。 事实上,它非常顺利。 以下是在CoreAnimation工具中运行代码的屏幕截图:
正如您在顶部图表中看到的那样,FPS最高可达~60FPS并在整个手势中保持不变。 在底部的图表中,您可以看到大约16s的blip,这是图像首次加载的位置,但您可以看到帧速率没有下降。 仅从视觉检查,我看到选择层相交,并且在第一个交叉点和图像外观之间存在一个小但可观察到的延迟。 据我所知,后台加载代码正在按预期执行。
我希望我能帮助你更多,但我只是没有看到问题。
您可以使用AFNetworking库,通过导入类别
“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不断更新。 以下是解决我的问题的简单教程链接:
这是好方法:
-(void)updateImageViewContent {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage * img = [UIImage imageNamed:@"background.jpg"];
[[self imageView] setImage:img];
});
}
为什么不使用像AsyncImageView这样的第三方库? 使用它,您所要做的就是声明您的AsyncImageView对象并传递您要加载的url或图像。 在图像加载期间将显示活动指示器,并且不会阻止UI。
- (void)touchesBegan:在主线程中调用。 通过调用dispatch_async(dispatch_get_main_queue),您只需将块放入队列中。 当队列准备好时(即系统结束并处理您的触摸),此块将由GCD处理。 这就是为什么你不能看到你的woodenTile被加载并分配给self.image,直到你释放你的手指并让GCD处理所有在主队列中排队的块。
更换:
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
});
通过:
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
应该解决你的问题...至少对于展示它的代码。
考虑使用SDWebImage :它不仅可以在后台下载和缓存图像,还可以加载和渲染它。
我已经在tableview中使用了它,结果很好,即使在下载之后也有很慢的加载速度。
https://github.com/nicklockwood/FXImageView
这是一个可以处理后台加载的图像视图。
用法
FXImageView *imageView = [[FXImageView alloc] initWithFrame:CGRectMake(0, 0, 100.0f, 150.0f)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.asynchronous = YES;
//show placeholder
imageView.processedImage = [UIImage imageNamed:@"placeholder.png"];
//set image with URL. FXImageView will then download and process the image
[imageView setImageWithContentsOfURL:url];
要获取文件的URL,您可能会发现以下内容:
在应用程序中使用AFNetwork时,您无需使用任何块来加载映像,因为AFNetwork为其提供了解决方案。 如下:
#import "UIImageView+AFNetworking.h"
和
Use **setImageWithURL** function of AFNetwork....
谢谢
我实现它的一种方法是以下:(虽然我不知道它是否是最好的一个)
首先,我使用串行异步或并行队列创建队列
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;
});
});
iOS 15 中的UIImage
引入了一组方法,用于在后台线程上异步解码图像和创建缩略图
func prepareForDisplay(completionHandler: (UIImage?) -> Void)
异步解码图像并提供新图像以在视图和动画中显示。
func prepareThumbnail(of: CGSize, completionHandler: (UIImage?) -> Void)
在后台线程上异步创建指定大小的缩略图。
如果您需要更多地控制您希望解码发生的位置,例如特定队列,您还可以使用一组类似的同步API:
func prepareForDisplay() -> UIImage?
func 准备Thumbnail(of: CGSize) -> UIImage?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.