LoginSignup
8
6

More than 5 years have passed since last update.

BTKCollectionViewFlowLayoutのチューニング

Posted at

前回に続いて、UICollectionViewFlowLayout拡張の話です。
UICollectionViewFlowLayoutの拡張

Sticky Sessionの実装方法

UICollectionViewFlowLayoutのレイアウトは静的なもので、スクロール時にレイアウトの再計算は必要ありません。しかし、Sticky SessionではHeader/Footerの位置がスクロールに応じて変化するため、スクロールに応じてレイアウトを再計算する必要があります。
このような場合には、shouldInvalidateLayoutForBoundsChangeからYESを返すことで、常にレイアウトを再計算することができます。

- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

あとは頑張ってHeader/Footerの位置を計算するだけです。
contentInsets、sectionInsets、header/footerの有無を考えてコードを書く必要がありますが、順につぶしていけば実装できます。

パフォーマンス上の問題

このアプローチで実装すると、sticky view使用時のフレームレートが半分以下に落ちます。
いままで不要だったレイアウト計算が毎回必要になるので、当然の結果ですね。しかし、エンジニアとしてフレームレートが半分になるのは我慢できません。できませんよね?

解決策

原因がはっきりしている以上、解決策も簡単に思いつきます。スクロール時にはHeader/Footerのみを再計算すればいいわけです。これが思ったより面倒でした。
関係するコードを抜粋します。

鍵になるのは[self invalidationContextForBoundsChange:CGRectZero]で空のcontextを作っている部分です。これを元にheader/footerを追加することで、header/footerだけを更新するcontextを生成しています。

- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    UICollectionViewLayoutInvalidationContext *context = [self invalidationContextForBoundsChange:CGRectZero];
    [self invalidateLayoutWithContext:context];
    return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}

- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds
{
    UICollectionViewLayoutInvalidationContext *context = [super invalidationContextForBoundsChange:newBounds];
    NSInteger numOfSections = self.collectionView.numberOfSections;
    NSMutableArray *indexePaths = NSMutableArray.new;
    for(NSInteger s = 0 ; s < numOfSections ; s++ ){
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:s];
        [indexePaths addObject:indexPath];
    }
    [context invalidateSupplementaryElementsOfKind:UICollectionElementKindSectionHeader
                                      atIndexPaths:indexePaths];
    [context invalidateSupplementaryElementsOfKind:UICollectionElementKindSectionFooter
                                      atIndexPaths:indexePaths];
    return context;
}

互換性

UICollectionViewLayoutInvalidationContextで、個別のSupplemental Viewを指定する機能はiOS8からしか使用できません。
そのためiOS7用ではパフォーマンスが落ちるかと思ったのですが、「指定できない==常にすべて更新」という意味らしく、新メソッドを呼び出さないようにするだけで動作しました。

最後に

BTKCollectionViewFlowLayoutのコードを見てもらうとわかりますが、実際には結構面倒な作業でした。Apache Licenseなので、よかったら使ってみてください。
BTKCollectionViewFlowLayout on GitHub

余談

PodspecがiOS6.0からになってる。。orz
iOS7.0以降でしか動きません。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6