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 URLがあります)。
そのイメージを非同期にロードする方法はありますか? tableView:cellForRowAtIndexPathが非常に頻繁に呼び出されるため、難しいと思います。
ブロックと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が使用されていることを前提としています。そうでない場合は、オブジェクトの独自のリリースを管理する必要があります)
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
迅速:
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
})
失敗のケースを考慮します。
- (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);
});
});
}
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
}
})
はい、比較的簡単です。アイデアは次のようなものです:
[self.tableView reloadData]
私はこのアプローチを何度もテストしてきましたが、すばらしい結果が得られました。非同期部分のヘルプ/例が必要な場合(そのためにgcdを使用することをお勧めします)、教えてください。
Appleこの目的のために提供されるサンプルコードを使用する場合、完全に簡単に行うことができます:サンプルコード: Lazy Image
委任rowforcellを見て、icondownloaderファイルをプロジェクトに追加するだけです。
必要な変更は、オブジェクトでapprecordオブジェクトを変更することだけです。
SDWebImageと他のサードパーティは素晴らしいソリューションかもしれませんが、サードパーティのAPIを使用したくない場合は、独自のソリューションを開発することもできます。
これを参照してください 遅延読み込みに関するチュートリアル これは、テーブルビュー内でデータをモデル化する方法についても説明しています。
おそらく、UIImageViewをサブクラス化する必要があります。最近、この特定のタスクを説明する単純なプロジェクトを作成しました-バックグラウンドの非同期イメージの読み込み-GitHubで 私のプロジェクトで を見てください。具体的には、 KDImageView クラスを見てください。