SDWebImageのメモ

# SDWebImageのメモ

[SDWebImage](https://github.com/rs/SDWebImage)は、Web上の画像をダウンロードして使うのに便利な、定番っぽいライブラリの一つです。

## ダウンロードが完了した時にフェードイン

キャッシュにあった場合は何もしないですぐに表示、新たにダウンロードした場合のみ、フェードインで表示するようにします。

```swift
  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 (追記)

ちょっとハマったので追記。

```swift
    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

```swift
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 (追記)

[ドキュメント](https://github.com/SDWebImage/SDWebImage/wiki/How-to-use#using-sdwebimageprefetcher)によれば、次のprefetchを行っても前のprefetchはキャンセルされなくなり、キャンセルしたい場合は明示的に行うようになったようです。

### Version 3.x

実装を見ると、prefetchURLs()を呼ぶと、以前にプリフェッチ中だったURLsは全てキャンセルされるようです。なので、例えば写真のURLの入ったWebAPIのレスポンスを処理した後のタイミングなどで、まとめてprefetchするのがよさげです。

```swift
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がキャンセルされるのが嫌なら、外部でキューを管理する、という方法もいいかもしれません。

```swift
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で呼ばれるようなので、すり抜けも起こらなさそうです。