iOS: GIF 画像のロードに関する考え

序文

最近、プロジェクトにアニメーション効果を追加する必要がありましたが、デザイナーは Lottie アニメーションをエクスポートした後、その効果が実現できないことに気づき、GIF 画像を使用して効果を実現したいと考えました。
しかし、gif画像をプロジェクトに入れて実行するときにいくつか問題が見つかったので、ここでgif画像の読み込みに関する問題を整理します!
以下の観点は、単純かつ不正確なテストからの著者の結論です。間違いがある場合はご容赦ください。議論へようこそ!

GIFの原理

GIF の正式名は Graphics Interchange Format で、Graphics Interchange Format に翻訳でき、インデックス付きカラー イメージをハイパーテキスト マークアップ言語 (Hypertext Markup Language) で表示するために使用され、インターネットやその他のオンライン サービス システムで広く使用されています。GIF は、Compu Serve Corporation が著作権を持つパブリック画像ファイル形式の標準です。GIF Baidu Encyclopedia を
詳しく読んでください。詳しく調べる必要はありません。単純に、GIF をループするスライドショーとして理解してください (個人的な意見)。

ロード方法

  1. UIイメージビュー
  2. SDWebImage
  3. Q拡張機能
  4. WKWebView
  5. YY画像
  6. QMUI では
    次の 6 つの方法について説明し、実際のニーズに応じて表示スキームを選択しますが、個人的には次のようにYYimageを使用することをお勧めします。

UIイメージビュー

システムが提供するUIimageView は、スライドショーの再生に似た複数の画像の再生をサポートしていますが、この方法では、最初に *.gif* ファイルを単一のフレーム画像に抽出してから再生する必要があります。とても面倒です!ただし、実装は非常に簡単で、切り替える写真が数枚だけであれば、選択することができます。

#pragma mark - eg
UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
gifImageView.animationImages = @[];   // 存放每一帧的UIImage的数组
gifImageView.animationDuration = 5;   // 执行一次完整动画所需的时长
gifImageView.animationRepeatCount = 1;// 动画重复次数
[gifImageView startAnimating];
[self.view addSubView:gifImageView];
#pragma mark - UIImageView Api
@property (nonatomic, getter=isHighlighted) BOOL highlighted API_AVAILABLE(ios(3.0)); // default is NO
// these allow a set of images to be animated. the array may contain multiple copies of the same
@property (nullable, nonatomic, copy) NSArray<UIImage *> *animationImages; // The array must contain UIImages. Setting hides the single image. default is nil
@property (nullable, nonatomic, copy) NSArray<UIImage *> *highlightedAnimationImages API_AVAILABLE(ios(3.0)); // The array must contain UIImages. Setting hides the single image. default is nil
@property (nonatomic) NSTimeInterval animationDuration;         // for one cycle of images. default is number of images * 1/30th of a second (i.e. 30 fps)
@property (nonatomic) NSInteger      animationRepeatCount;      // 0 means infinite (default is 0)
// When tintColor is non-nil, any template images set on the image view will be colorized with that color.
// The tintColor is inherited through the superview hierarchy. See UIView for more information.
@property (null_resettable, nonatomic, strong) UIColor *tintColor API_AVAILABLE(ios(7.0));
- (void)startAnimating;
- (void)stopAnimating;
@property(nonatomic, readonly, getter=isAnimating) BOOL animating;



    NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"<#gifName#>" withExtension:@"gif"]; //加载GIF图片
    CGImageSourceRef gifSource = CGImageSourceCreateWithURL((CFURLRef) fileUrl, NULL);           //将GIF图片转换成对应的图片源
    size_t frameCout = CGImageSourceGetCount(gifSource);                                         //获取其中图片源个数,即由多少帧图片组成
    NSMutableArray *frames = [[NSMutableArray alloc] init];                                      //定义数组存储拆分出来的图片
    for (size_t i = 0; i < frameCout; i++) {
    
    
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i, NULL); //从GIF图片中取出源图片
        UIImage *imageName = [UIImage imageWithCGImage:imageRef];                  //将图片源转换成UIimageView能使用的图片源
        [frames addObject:imageName];                                              //将图片加入数组中
        CGImageRelease(imageRef);
    }
    UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:CGRectMake(<#x#>, <#y#>, <#w#>, <#h#>)];
    gifImageView.animationImages = frames; //将图片数组加入UIImageView动画数组中
    gifImageView.animationDuration = 0.15; //每次动画时长
    [gifImageView startAnimating];         //开启动画,此处没有调用播放次数接口,UIImageView默认播放次数为无限次,故这里不做处理
    [self.view addSubview:gifImageView];

SDWebImage

SDWebImage は画像を読み込むためのサードパーティ製の共通ライブラリと言えますが、これを使用してみましたが、メモリ使用量に重大な問題があることがわかりました。GIF を一度読み込むと、メモリが直接200M近く増加し、フレーム数が多い場合には100M以上。これは絶対にお勧めできません。

#import <SDWebImage/UIImage+GIF.h>
#pragma mark - eg
// 注意
UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
NSString *filePath = [[NSBundle bundleWithPath:[[NSBundle mainBundle] bundlePath]] pathForResource:@"test" ofType:@"gif"];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
gifImageView.image = [UIImage sd_imageWithGIFData:imageData];
[self.view addSubView:gifImageView];

Q拡張機能

QExtensionは多くのクラスを拡張し、多くのメソッドを提供する 3 部構成ですが、使用する人は限られており、使用するとSDWebImageと同じメモリ問題が発生するため、

#import <QExtension/UIImage+GIF.h>
#pragma mark - eg
// 注意
UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
NSString *filePath = [[NSBundle bundleWithPath:[[NSBundle mainBundle] bundlePath]] pathForResource:@"test" ofType:@"gif"];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
gifImageView.image = [UIImage q_gifImageWithData:imageData];
[self.view addSubView:gifImageView];

WKWebView

システムのWKWebView はGIFデータも表示できるので、メモリに優しく、急増することもなく、テスト中に7M増やしました。使い方も非常に簡単で、需要が少ないため、これ以上サードパーティを導入したくない場合は、使用を検討できます。

#import <WebKit/WebKit.h>
#pragma mark - eg
WKWebView *gifView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
NSString *filePath = [[NSBundle bundleWithPath:[[NSBundle mainBundle] bundlePath]] pathForResource:@"test" ofType:@"gif"];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
[gifView loadData:imageData MIMEType:@"image/gif" characterEncodingName:@"" baseURL:[NSURL URLWithString:@""]];
[self.view addSubView:gifView];

YY画像

YYImageも有名なサードパーティと言え、 UIimageViewUIImageの優れた拡張機能を備えておりUIimageViewの拡張機能なので、UIimageViewの特徴を全て備えており、互換性も非常に良いです!テストしたところ、メモリが6M増加しました。

#import <YYImage/YYImage.h>
#pragma mark - eg
UIImageView *gifImageView = [[YYAnimatedImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
UIImage *img = [YYImage imageNamed:@"test.gif"];
gifImageView.image = img;
[self.view addSubView:gifImageView];

GIF画像を直接バンドルに入れるだけです。

QMUI

Tencent が開発した QMUI ライブラリにも GIF を読み込むメソッドが用意されており、よく考えて学習したところ、内部実装は次のようになっていることがわかりました。

+ (UIImage *)qmui_animatedImageWithData:(NSData *)data scale:(CGFloat)scale {
    
    
    // http://www.jianshu.com/p/767af9c690a3
    // https://github.com/rs/SDWebImage
    if (!data) {
    
    
        return nil;
    }
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    size_t count = CGImageSourceGetCount(source);
    UIImage *animatedImage = nil;
    scale = scale == 0 ? ScreenScale : scale;
    if (count <= 1) {
    
    
        animatedImage = [[UIImage alloc] initWithData:data scale:scale];
    } else {
    
    
        NSMutableArray<UIImage *> *images = [[NSMutableArray alloc] init];
        NSTimeInterval duration = 0.0f;
        for (size_t i = 0; i < count; i++) {
    
    
            CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
            duration += [self qmui_frameDurationAtIndex:i source:source];
            UIImage *frameImage = [UIImage imageWithCGImage:image scale:scale orientation:UIImageOrientationUp];
            [images addObject:frameImage];
            CGImageRelease(image);
        }
        if (!duration) {
    
    
            duration = (1.0f / 10.0f) * count;
        }
        animatedImage = [UIImage animatedImageWithImages:images duration:duration];
    }
    CFRelease(source);
    return animatedImage;
}

その本質は、NSData のメソッドを使用して UIImage をロードすることです。

単純な最適化

在2023年3月25日,进行Timer Profiler检查时,发现YYImage显示GIF,即使进入后台依旧在占用线程。
タイマープロファイル
在使用QMUI进行同一张GIF加载对比后发现
比較した
QMUI对于加载时间和进入后台后的表现优于YYImage

#import <QMUIKit/QMUIKit.h>
#pragma mark - eg
UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
gifImageView.image = [UIImage qmui_animatedImageNamed:@"test.gif"];
[self.view addSubView:gifImageView];

但是,内存方面,YYImage却大大的优于QMUI
メモリ比較
对于进入后台后,YYAnimatedImageView一直在运行的解决方案:
使用进入前后台的监听方式,在进入后台时暂停动画的播放,在进入前台后再播放。

- (void)applicationDidBecomeActive{
    
    
    [self.gifImageView startAnimating];
}

- (void)applicationDidEnterBackground{
    
    
    [self.gifImageView stopAnimating];
}

- (void)viewDidLoad {
    
    
    [super viewDidLoad];
    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.grayColor;
    [self.view addSubview:self.gifImageView];
}

- (UIImageView *)gifImageView {
    
    
    if (!_gifImageView) {
    
    
        _gifImageView = [[YYAnimatedImageView alloc] init];
        _gifImageView.frame = CGRectMake(0, 0, 200, 200);
        _gifImageView.center = self.view.center;
        _gifImageView.image = [YYImage imageNamed:@"418k.gif"];
    }
    return _gifImageView;
}

- (void)dealloc {
    
    
    [NSNotificationCenter.defaultCenter removeObserver:self];
    NSLog(@"销毁了");
}

解決

总结

  1. UIImageView
    优点:原生
    缺点:性能较差
  2. SDWebImage
    优点:使用简单方便
    缺点:需要引入三方、内存爆炸!!!
  3. QExtension
    优点:使用简单方便
    缺点:需要引入三方、不常用、内存爆炸!!!
  4. WKWebView
    优点:原生、使用简单、兼容性好
    缺点:无明显缺点
  5. YYImage
    优点:使用简单、兼容性好,可以控制暂停和播放
    缺点:需要引入三方,在后台依旧占用线程,需要自己解决一下。
  6. QMUI
    优点:使用简单,本质就是NSdata转UIImage
    缺点:需要引入三方、内存占用很高,无法自由控制播放

素材

1
2
3

学习资料

「iOS クライアント アニメーションの最適化実践」 - オリジナル wyanwan Tencent Music Technology Team 2023-05-09 12:02

「iOS で GIF アニメーションを再生するいくつかの方法」

おすすめ

転載: blog.csdn.net/qq_38718912/article/details/123399925
GIF