読者です 読者をやめる 読者になる 読者になる

Keep It Real BLOG

ソフトウェアエンジニア。1児の父。 酒、ラーメン、サッカー好き。旅行も好きですが、普段は出不精で大抵たまプラーザ界隈に居ます。

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

f:id:naohide_a:20151202194822p:plain

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の非同期処理を行ってサイズ計算するようにして、そのセルのサイズをキャッシュするようにし、改善しました。

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