SDWebImageのメモ
SDWebImageは、Web上の画像をダウンロードして使うのに便利な、定番っぽいライブラリの一つです。
ダウンロードが完了した時にフェードイン
キャッシュにあった場合は何もしないですぐに表示、新たにダウンロードした場合のみ、フェードインで表示するようにします。
imageView.setShowActivityIndicatorView(true)
imageView.sd_setImageWithURL(thumbnailURL) {
image, error, cacheType, imageUrl in
imageView.setShowActivityIndicatorView(false)
if error != nil { return }
if image != nil && cacheType == .None {
imageView.alpha = 0
UIView.animateWithDuration(0.5) {
imageView.alpha = 1
}
}
}
オリジナルサイズの画像をロードする間、サムネールがキャッシュにあれば、それをplaceholderに使う
オリジナル画像と同じアスペクト比のサムネールがあれば、先に粗い写真を表示しておいて、オリジナルのダウンロードが終わると、写真の解像度が上がる、といった事をやりたかったので、調べてみました。
sd_setImageWithURL()は、placeholderにUIImageしか受け取ってくれないので、メモリキャッシュのUIImageを同期で取り出す方法を調べてみました。
Version 5.x (追記)
ちょっとハマったので追記。
let manager = SDWebImageManager.sharedManager()
let key = manager.cacheKeyForURL(thumbnailURL)
let thumbnailImage = (manager.imageCache as? SDImageCache)?.imageFromMemoryCache(forKey: key) ?? emptyImage
cell.imageView.sd_setImageWithURL(originalPhotoURL, placeholderImage: thumbnailImage)
Version 3.x
import WebImage
class PhotoCollectionViewController: UICollectionViewController {
// キャッシュがない場合の画像
let emptyImage = UIImage()
// ...
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PhotoCollectionViewCell", forIndexPath: indexPath) as! PhotoCollectionViewCell
cell.imageView.contentMode = .ScaleAspectFit
let thumbnailURL = photos[indexPath.row].getThumbnailURL()
// ここから
let manager = SDWebImageManager.sharedManager()
let key = manager.cacheKeyForURL(thumbnailURL)
let thumbnailImage =
manager.imageCache.imageFromMemoryCacheForKey(key) ?? emptyImage
cell.imageView.sd_setImageWithURL(originalPhotoURL, placeholderImage: thumbnailImage)
return cell
}
}
サムネールのプリフェッチ
SDWebImageには、画像をあらかじめバックグラウンドでダウンロードしてキャッシュしてくれる機能があります。サムネールをサクサク表示したい時や、上記のように、とりあえず粗い画像でもすぐ出したい、といった時に便利です。
Version 5.x (追記)
ドキュメントによれば、次のprefetchを行っても前のprefetchはキャンセルされなくなり、キャンセルしたい場合は明示的に行うようになったようです。
Version 3.x
実装を見ると、prefetchURLs()を呼ぶと、以前にプリフェッチ中だったURLsは全てキャンセルされるようです。なので、例えば写真のURLの入ったWebAPIのレスポンスを処理した後のタイミングなどで、まとめてprefetchするのがよさげです。
import WebImage
class App {
let imagePrefetcher = SDWebImagePrefetcher.sharedImagePrefetcher()
// 沢山の写真のURLが入ったWebAPI
func getPhotos() {
var thumbnailURLs = [NSURL]()
let task = getPhotosTask(params) {
// response処理の中でthumbnailのURLを集めておく
thumbnailURLs.append(thumbnailURL)
// ...
self.imagePrefetcher.prefetchURLs(thumbnailURLs)
}
}
}
追記
上記でも、やはり次のAPI呼び出しで、前のprefechがキャンセルされてしまうので、どうしてもprefetchがキャンセルされるのが嫌なら、外部でキューを管理する、という方法もいいかもしれません。
import WebImage
class App {
var prefetchURLs = [NSURL]()
let imagePrefetcher = SDWebImagePrefetcher.sharedImagePrefetcher()
func loadPhotos() {
let task = loadPhotosTask(params) {
// response処理の中でthumbnailのURLを集めておく
self.prefetchURLs.append(thumbnailURL)
// ...
self.startPrefetch()
}
}
func startPrefetch() {
self.imagePrefetcher.prefetchURLs(prefetchURLs, progress: {
finished, total in
assert(NSThread.isMainThread())
print("\(finished)/\(total)")
}, completed: {
finished, skipped in
assert(NSThread.isMainThread())
print("Prefetch finished: \(finished) skipped: \(skipped)")
self.prefetchURLs.removeAll()
})
}
}
コールバックもMain Threadで呼ばれるようなので、すり抜けも起こらなさそうです。