セルサイズがランダムなUICollectionViewのスクロールを爆速化

Swift

UICollectionViewのスクロールが遅いのには理由があった

UICollectionViewで、サイズがランダムなセルを組み込んで使っていたのですが、かなり苦戦しまして、スクロールに突っ掛かりがあったり、スクロールで次のコンテンツを読み込んだ際にチラつきが出たりで、大変でした。。

まずは、スクロールの突っ掛かりが、かなり改善されたので、そちらの方法を紹介します。

セルの高さはキャッシュして持っておく

セルの高さがランダムであると、スクロール時に毎回サイズを計算しに行かなければなりません。そして、そのサイズ計算の処理に時間が掛かっていると、かなりスクロールに突っ掛かりを感じることになります。UICollectionViewが勝手にキャッシュして上手いことやってくれるなんて、そんな美味しい話はないんですね…。

そこで、セルの高さをController毎に、NSCacheを使ってキャッシュさせることにしました。

実際の書き方としては、以下のとおりです。

private let cache = NSCache()
override func viewDidLoad() {
    super.viewDidLoad()
    // キャッシュ削除
    cache.removeAllObjects()
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    // キャッシュされたサイズを持っていれば、そのサイズを返す
    if let cachedCellSize = cache.objectForKey(String(indexPath.section) + String(indexPath.row)) as? NSValue {
        return cachedCellSize.CGSizeValue()
    } else {
        // サイズの計算
        let cellSize: CGSize = ContentCell.sizeForRow(collectionView: collectionView)
        // サイズをキャッシュ
        cache.setObject(NSValue(CGSize: cellSize), forKey: String(indexPath.section) + String(indexPath.row))
        return cellSize
    }
}

これは、かなり効果絶大で、これで圧倒的に早くなりました。

画像は非同期で読むことは当然ながら、高さと幅を持っておく

自分が作ったアプリでは、画像の高さもランダムなのですが、非同期で画像を取得すると、高さ計算が画像取得後になる為、どうしても無駄な処理が増え、スピードが遅くなったり、ガクついたりするということが起きてしまいました。

結果として、サーバ側から、画像と共に高さと幅の情報も同時に持たせるようにして、改善を行いました。

そうしたことで、画像が表示される前のデフォルト表示も良い感じになりました。

重いsectionは、非同期でpreloadしておく

sectionの一部で重い処理をしていた為、そこまでスクロールしてからサイズの計算処理を行う(sizeForItemAtIndexPathでは、ディスプレイ内に表示されてる部分を計算しにいく)と、そこで若干スクロールが止まってしまうという現象が起きていました。

そこで、その重いsectionに関しては、 viewDidLoad 内でpreloadの非同期処理を行ってサイズ計算するようにして、そのセルのサイズをキャッシュするようにし、改善しました。

以上の細かい改善の積み重ねで、かなりスクロールが滑らかになり、爆速化出来ました!

最後に

iOSアプリ開発ノウハウが溜まっていない中、ここまで高速化が出来て良かったです。
Instrumentsでどこがボトルネックになっているのかチェックすること大事ですね。

コメントを残す