我正在使用iOS 5.0 SDK和XCode 4.2开发iOS 4应用程序。
我必须在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;
}
但这一行:
cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]];
它太慢了(entry.photo有一个http网址)。
有没有办法异步加载该图像?我认为很难,因为tableView:cellForRowAtIndexPath经常被调用。
答案 0 :(得分:81)
我使用块和GCD编写了一个自定义类来执行此操作:
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
#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
在你的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;
}
}];
}
速度非常快,可以在不影响TableView的UI或滚动速度的情况下加载图像。
***注 - 此示例假定正在使用ARC。如果没有,您将需要在对象上管理自己的版本)
答案 1 :(得分:52)
在iOS 6及更高版本中,dispatch_get_current_queue提供弃用警告。
这是一个替代方案,它是上述@ElJay答案和@khanlou here的文章的综合。
在UIImage上创建一个类别:
<强>的UIImage + Helpers.h 强>
@interface UIImage (Helpers)
+ (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback;
@end
<强>的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
答案 2 :(得分:28)
答案 3 :(得分:6)
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)
}
})
}
})
}
}
用法:
// 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
})
答案 4 :(得分:3)
考虑失败案例。
- (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);
});
});
}
答案 5 :(得分:3)
Swift 4 |图像的异步加载
创建一个名为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()
}
}
在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
}
})
答案 6 :(得分:2)
是的,它相对容易。这个想法是这样的:
[self.tableView reloadData]
我已经多次测试过这种方法并且效果很好。如果您需要有异步部分的任何帮助/示例(我建议使用gcd)请告诉我。
答案 7 :(得分:1)
如果您使用Apple提供的示例代码,则可以轻松完成此操作: 示例代码: Lazy Image
只需查看委托rowforcell并将icondownloader文件添加到项目中即可。
您唯一需要做的改变就是用您的对象更改欣赏对象。
答案 8 :(得分:1)
虽然SDWebImage和其他第三方可能是很好的解决方案,但如果您不热衷于使用第三方API,您也可以开发自己的解决方案。
请参阅此tutorial about lazy loading,其中还讨论了如何在表视图中建模数据。
答案 9 :(得分:1)
AsyncImage
可以同步加载和显示图片。在 iOS 15 之后正式引入
cell.photo = AsyncImage(url: URL(string: entry.photo))
.frame(width: 200, height: 200)
它还支持:
在doc中查看更多
答案 10 :(得分:0)
您可能必须继承您的UIImageView。我最近做了一个简单的项目来解释这个特定的任务 - 后台异步图像加载 - 看看GitHub上的at my project。具体来说,请查看KDImageView类。
答案 11 :(得分:0)
使用以下代码片段将图像加载到imageview中
func imageDownloading() {
DispatchQueue.global().async {
let url = URL(string: "http://verona-api.municipiumstaging.it/system/images/image/image/22/app_1920_1280_4.jpg")!
do {
let data = try Data(contentsOf: url)
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
} catch {
print(error.localizedDescription)
}
}
}