ASImageNodeのすゝめ
ASImageNode
は、Facebookのオープンソース非同期UIライブラリAsyncDisplayKit
に含まれるUIImageView
相当のコンポーネントです。
このコンポーネントを使用することで、かんたんに画像表示のパフォーマンスを改善できることをご紹介します。
UIImageView の問題点
UIImageView
は画像を表示するのに必要なすべての処理をメインスレッドで行ってしまいます。画像を表示するための処理は大きなバイナリデータを扱うため、非常に重たい処理です。メインスレッドでこれを行うと簡単にUIをブロックしてしまいます。
表示する画像が重かったり、または UICollectionView
などで画像を表示していると、顕著に感じられると思います。
対して、ASImageNode
は画像を表示するための下準備すべて(デコード、レンダリング)をバックグラウンドスレッドで行います。メインスレッドは ASImageNode
よりレンダリング済みのデータを受け取り、View にベタッと貼り付けるだけ。メインスレッドの処理量が大幅に削減されています。
実際の効果(続報あり?)
いまのプロジェクトでは、4列のUICollectionView
に200x200ほどの画像を順次 - setImage:
するだけで20-30fps程度まで下がってしまっていました。
体感的にも、カクカクしています。
今回、ASImageNode
を使って描画を行うことで、60fps付近をキープするようになりました。
体感的には、スクロールでカクカクを全く感じなくなりました。
コード
UICollectionView でのコードを掲載します。
// KIExampleCollectionViewCell @interface KIExampleCollectionViewCell () @property ASImageNode *imageNode; @end - (void)prepareForReuse { [super prepareForReuse]; // (1) で追加した View を消す。`UIImageView-setImage:nil` の代わり。 [_imageNode.view removeFromSuperview]; } - (void)setImage:(UIImage *)image { if (image == nil) { return; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (!_imageNode) { _imageNode = [[ASImageNode alloc] init]; _imageNode.frame = self.contentView.frame; } _imageNode.image = image; dispatch_async(dispatch_get_main_queue(), ^{ // (1) _imageNode.view で描画結果の View を取得できます。 [self.contentView addSubview:_imageNode.view]; }); }); }