ios 将图像异步加载到 UIImage 中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9786018/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Loading an image into UIImage asynchronously
提问by VansFannel
I'm developing an iOS 4 application with iOS 5.0 SDK and XCode 4.2.
我正在使用 iOS 5.0 SDK 和 XCode 4.2 开发 iOS 4 应用程序。
I have to show some post blogs into a UITableView. When I have retreived all web service data, I use this method to create an UITableViewCell:
我必须在 UITableView 中显示一些帖子博客。当我检索到所有 Web 服务数据时,我使用此方法创建一个 UITableViewCell:
- (BlogTableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = @"BlogCell";
BlogTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BlogTableViewCell" owner:nil options:nil];
for(id currentObject in topLevelObjects)
{
if ([currentObject isKindOfClass:[BlogTableViewCell class]])
{
cell = (BlogTableViewCell *)currentObject;
break;
}
}
}
BlogEntry* entry = [blogEntries objectAtIndex:indexPath.row];
cell.title.text = entry.title;
cell.text.text = entry.text;
cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]];
return cell;
}
But this line:
但是这一行:
cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]];
it is so slow (entry.photo has a http url).
它太慢了(entry.photo 有一个 http url)。
Is there any way to load that image asynchronously? I think it is difficult because tableView:cellForRowAtIndexPath is called very often.
有没有办法异步加载该图像?我认为这很困难,因为 tableView:cellForRowAtIndexPath 经常被调用。
采纳答案by tarmes
Take a look at SDWebImage:
看看 SDWebImage:
https://github.com/rs/SDWebImage
https://github.com/rs/SDWebImage
It's a fantastic set of classes that handle everything for you.
这是一个梦幻般的组类手柄一切都是为了你。
Tim
蒂姆
回答by LJ Wilson
I wrote a custom class to do just this, using blocks and GCD:
我编写了一个自定义类来做到这一点,使用块和 GCD:
WebImageOperations.h
WebImageOperations.h
#import <Foundation/Foundation.h>
@interface WebImageOperations : NSObject {
}
// This takes in a string and imagedata object and returns imagedata processed on a background thread
+ (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage;
@end
WebImageOperations.m
WebImageOperations.m
#import "WebImageOperations.h"
#import <QuartzCore/QuartzCore.h>
@implementation WebImageOperations
+ (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage
{
NSURL *url = [NSURL URLWithString:urlString];
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
dispatch_async(downloadQueue, ^{
NSData * imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(callerQueue, ^{
processImage(imageData);
});
});
dispatch_release(downloadQueue);
}
@end
And in your ViewController
在你的 ViewController 中
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Pass along the URL to the image (or change it if you are loading there locally)
[WebImageOperations processImageDataWithURLString:entry.photo andBlock:^(NSData *imageData) {
if (self.view.window) {
UIImage *image = [UIImage imageWithData:imageData];
cell.photo.image = image;
}
}];
}
It is very fast and will load the images without affecting the UI or scrolling speed of the TableView.
它非常快,并且可以在不影响 TableView 的 UI 或滚动速度的情况下加载图像。
*** Note - This example assumes ARC is being used. If not, you will need to manage your own releases on objects)
*** 注意 - 此示例假设正在使用 ARC。如果没有,您将需要管理自己的对象发布)
回答by bbrame
In iOS 6 and later dispatch_get_current_queue gives deprecation warnings.
在 iOS 6 及更高版本中 dispatch_get_current_queue 给出弃用警告。
Here is an alternative that is a synthesis of the @ElJay answer above and the article by @khanlou here.
这里是作为@ElJay答案上述和由@khanlou制品的合成的替代这里。
Create a category on UIImage:
在 UIImage 上创建一个类别:
UIImage+Helpers.h
UIImage+Helpers.h
@interface UIImage (Helpers)
+ (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback;
@end
UIImage+Helpers.m
UIImage+Helpers.m
#import "UIImage+Helpers.h"
@implementation UIImage (Helpers)
+ (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData * imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
callback(image);
});
});
}
@end
回答by Alexander Volkov
Swift:
迅速:
extension UIImage {
// Loads image asynchronously
class func loadFromURL(url: NSURL, callback: (UIImage)->()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
let imageData = NSData(contentsOfURL: url)
if let data = imageData {
dispatch_async(dispatch_get_main_queue(), {
if let image = UIImage(data: data) {
callback(image)
}
})
}
})
}
}
Usage:
用法:
// First of all remove the old image (required for images in cells)
imageView.image = nil
// Load image and apply to the view
UIImage.loadFromURL("http://...", callback: { (image: UIImage) -> () in
self.imageView.image = image
})
回答by jose920405
Considering Failure Case.
考虑失败案例。
- (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSError * error = nil;
NSData * imageData = [NSData dataWithContentsOfURL:url options:0 error:&error];
if (error)
callback(nil);
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
callback(image);
});
});
}
回答by Akshay Sarda
Swift 4 | Async loading of image
斯威夫特 4 | 图像的异步加载
Make a new class named ImageLoader.swift
创建一个名为 ImageLoader.swift 的新类
import UIKit
class ImageLoader {
var cache = NSCache<AnyObject, AnyObject>()
class var sharedInstance : ImageLoader {
struct Static {
static let instance : ImageLoader = ImageLoader()
}
return Static.instance
}
func imageForUrl(urlString: String, completionHandler:@escaping (_ image: UIImage?, _ url: String) -> ()) {
let data: NSData? = self.cache.object(forKey: urlString as AnyObject) as? NSData
if let imageData = data {
let image = UIImage(data: imageData as Data)
DispatchQueue.main.async {
completionHandler(image, urlString)
}
return
}
let downloadTask: URLSessionDataTask = URLSession.shared.dataTask(with: URL.init(string: urlString)!) { (data, response, error) in
if error == nil {
if data != nil {
let image = UIImage.init(data: data!)
self.cache.setObject(data! as AnyObject, forKey: urlString as AnyObject)
DispatchQueue.main.async {
completionHandler(image, urlString)
}
}
} else {
completionHandler(nil, urlString)
}
}
downloadTask.resume()
}
}
To Use in your ViewController class:
要在您的 ViewController 类中使用:
ImageLoader.sharedInstance.imageForUrl(urlString: "https://www.logodesignlove.com/images/classic/apple-logo-rob-janoff-01.jpg", completionHandler: { (image, url) in
if image != nil {
self.imageView.image = image
}
})
回答by Alladinian
Yes it's relatively easy. The idea is something like:
是的,这相对容易。这个想法是这样的:
- Create a mutable array to hold your images
- Populate the array with placeholders if you like (or NSNull objects if you don't)
- Create a method to fetch your images asynchronously in the background
- When an image has arrived, swap your placeholder with the real image and do a
[self.tableView reloadData]
- 创建一个可变数组来保存您的图像
- 如果您愿意,可以使用占位符填充数组(如果您不喜欢,则使用 NSNull 对象)
- 创建一种在后台异步获取图像的方法
- 当图像到达时,将您的占位符与真实图像交换并执行
[self.tableView reloadData]
I have tested this approach many times and gives great results. If you need any help / examples with the async part (I recommend to use gcd for that) let me know.
我已经多次测试了这种方法并给出了很好的结果。如果您需要有关异步部分的任何帮助/示例(我建议为此使用 gcd),请告诉我。
回答by Nirav Bhatt
While SDWebImage and other 3rd party maybe great solutions, if you are not keen on using 3rd party APIs, you can develop your own solution too.
虽然 SDWebImage 和其他 3rd 方可能是不错的解决方案,但如果您不热衷于使用 3rd 方 API,您也可以开发自己的解决方案。
Refer to this tutorial about lazy loadingwhich also talks about how should you model your data inside table view.
回答by Firas KADHUM
you can easily do it perfectly if you use the sample code provided by Apple for this purpose: the sample code : Lazy Image
如果您为此目的使用Apple提供的示例代码,您可以轻松地做到这一点:示例代码: Lazy Image
Just look at the delegate rowforcell and add icondownloader files to you project.
只需查看委托 rowforcell 并将 icondownloader 文件添加到您的项目即可。
The only change you have to do is to change apprecord object with your object.
您唯一需要做的更改是使用您的对象更改 apprecord 对象。
回答by Kyr Dunenkoff
You'll probably have to subclass your UIImageView. I've recently made a simple project to explain this particular task - background asynchronous image loading - take a look at my projectat GitHub. Specifically, look at KDImageViewclass.
您可能必须对 UIImageView 进行子类化。我最近做了一个简单的项目来解释这个特定的任务——后台异步图像加载——看看我在 GitHub 上的项目。具体来说,看看KDImageView类。