うな(。・ε・。)

Android, iOS, AppEngine まわりのめもめも

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];
        });
    });
    
}