iOS: Thoughts on loading GIF images

foreword

Recently, an animation effect needs to be added to the project. After exporting the Lottie animation, the designer found that the effect cannot be achieved, so he wanted to use gif images to achieve it.
But I found some problems when I put the gif image into the project and run it, so I will sort out the problems about loading gif image here!
The following points of view are the author's conclusions from a simple and imprecise test. Please bear with me if there are any mistakes. Welcome to discuss!

Principle of GIF

The full name of GIF is Graphics Interchange Format, which can be translated into Graphics Interchange Format, used to display indexed color images in Hypertext Markup Language (Hypertext Markup Language), and is widely used on the Internet and other online service systems. GIF is a public image file format standard copyrighted by Compu Serve Corporation.
Just read the GIF Baidu Encyclopedia in detail , we don't have to delve into it. Simply understand GIF as a looping slideshow (personal opinion).

loading method

  1. UIImageView
  2. SDWebImage
  3. QExtension
  4. WKWebView
  5. YYImage
  6. QMUI
    discusses the following 6 methods, and chooses the display scheme according to the actual needs. I personally recommend using YYimage , as follows:

UIImageView

The UIimageView provided by the system supports the playback of multiple images, which is similar to playing a slideshow, but in this way, it is necessary to extract the *.gif* file into a single frame image first, and then play it, which is very troublesome! But the implementation is really simple. If it is only a few pictures to switch, you can still choose.

#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 can be said to be a common three-party library for loading pictures. I tried to use it, but found a serious memory usage problem. When loading a GIF once, the memory directly increases by nearly 200M and sometimes more than 100M when the number of frames is high . This is definitely not recommended!

#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];

QExtension

QExtension is a tripartite that extends many classes and provides many methods, but only a few people use it. It also has the same memoryproblem as SDWebImage when using it, so it is not recommended to use it!

#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

The WKWebView of the system can also display GIF data, and it is much friendlier in terms of memory, and it will not skyrocket. I increased it by 7M during the test . It is also very simple to use, only a small amount of demand, and if you don't want to introduce more third parties, you can consider using it!

#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];

YYImage

YYImage can also be said to be a very well-known third party. Ithas a good extension of UIimageView and UIImage , and the optimization of playing GIF pictures is also very good! Because it is an extension of UIimageView, it has all the characteristics of UIimageView, and the compatibility is very good! When I tested, the memory increased by 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];

Just put the GIF image directly in the Bundle!

QMUI

The QMUI library developed by Tencent also provides a method to load GIF. After thinking about learning, it is found that the internal implementation is as follows:

+ (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;
}

Its essence is to use the method of NSData to load UIImage.

simple optimization

On March 25, 2023, when checking the Timer Profiler, it was found that YYImage displayed GIF, and it was still occupying threads even after entering the background.
timer profiles
After using QMUI to compare the loading of the same GIF, it is found that
Compared
QMUI performs better than YYImage in terms of loading time and entering the background .

#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];

However, in terms of memory, YYImage is much better than QMUI for the solution that YYAnimatedImageView keeps running
memory comparison
after entering the background : use the monitoring method of entering the front and background, pause the playback of the animation when entering the background, and then play it after entering the foreground.

- (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(@"销毁了");
}

Solution

Summarize

  1. UIImageView
    Advantages: Native
    Disadvantages: Poor performance
  2. Advantages of SDWebImage
    : Simple and convenient to use
    Disadvantages: Need to introduce three parties, memory explosion! ! !
  3. Advantages of QExtension
    : Simple and convenient to use
    Disadvantages: Need to introduce three parties, not commonly used, memory explosion! ! !
  4. Advantages of WKWebView
    : native, easy to use, good compatibility
    Disadvantages: no obvious disadvantages
  5. Advantages of YYImage
    : easy to use, good compatibility, can control pause and play
    Disadvantages: need to introduce three parties, still occupy threads in the background, you need to solve it yourself.
  6. Advantages of QMUI
    : Easy to use, the essence is NSdata to UIImage
    Disadvantages: Three parties need to be introduced, memory usage is high, and playback cannot be freely controlled

material

1
2
3

learning materials

"iOS client animation optimization practice" - original wyanwan Tencent Music Technology Team 2023-05-09 12:02

"Several ways to play GIF animation on iOS"

Guess you like

Origin blog.csdn.net/qq_38718912/article/details/123399925