iOS:关于加载GIF图片的思考

前言

最近在项目中需要加入一个动画效果,设计师在导出Lottie动画后发现并不能达到效果,就想使用gif图片来实现。
但是将gif图片放入项目中运行时发现了一些问题,所以在这里整理一下有关加载gif图片的问题!
以下观点都是作者个人进行了不严谨的简单测试得出的结论,如有错误请多多包涵,欢迎讨论!

GIF原理

GIF的全称是Graphics Interchange Format,可译为图形交换格式,用于以超文本标志语言(Hypertext Markup Language)方式显示索引彩色图像,在因特网和其他在线服务系统上得到广泛应用。GIF是一种公用的图像文件格式标准,版权归Compu Serve公司所有。
详细去读GIF百度百科即可,我们不必深究。简单的将GIF理解为循环播放的幻灯片(个人见解)。

加载方式

  1. UIImageView
  2. SDWebImage
  3. QExtension
  4. WKWebView
  5. YYImage
  6. QMUI
    就以下6种方式进行讨论,根据实际需求情况选择显示方案,我个人还是推荐使用YYimage,具体如下:

UIImageView

系统提供的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有的100多M,这肯定是不推荐使用了!

#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是一个对很多类进行了拓展的三方,提供了很多方法,但是只有较少人使用,同样在使用时发生了和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];

YYImage

YYImage也可以说是很出名的一个三方了,它对UIimageViewUIImage都进行了很好的扩展,在播放GIF图片上的优化也是做的很好!由于是对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图片直接放在Bundle里就可以了!

QMUI

腾讯开发的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,即使进入后台依旧在占用线程。
timer profiler
在使用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 腾讯音乐技术团队 2023-05-09 12:02

《iOS播放GIF动画的几种方式》

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: iOS 富文本可以通过使用 NSAttributedString 和 NSTextAttachment 类加载 GIF 图片,达到将富文本与动态效果结合的效果。 首先,需要将 GIF 图片转换为 NSData 格式,可通过使用 NSData 的类方法 dataWithContentsOfFile 或者 dataWithContentsOfURL 来实现。 接着,创建 NSTextAttachment 实例,并以 NSData 格式将 GIF 图片作为参数传入。然后,创建 NSMutableAttributedString 实例,并将富文本内容包含在其中。 最后,使用 UILabel、UITextView 或者 UIWebView 等组件来展示富文本内容,并加以控制动态效果的播放周期及重复次数等属性,达到更丰富的用户体验。 需要注意的是,在使用富文本加载 GIF 图片时,应考虑对应用性能及网络耗费等问题进行优化调整,使得应用流畅稳定,并能够节省用户移动数据流量等资源。 ### 回答2: iOS开发中,要加载gif动图作为富文本,可以使用如下的步骤: 1. 引入SDWebImage库:在项目中添加SDWebImage库,这是一个常用的图片加载库,可以方便地加载并显示网络上的图片。 2. 下载gif图片:从网络上找到合适的gif图片,并将其下载到本地。 3. 将gif图片添加到富文本中:使用SDWebImage提供的方法将gif图片添加到富文本中,可以使用`UIImage sd_animatedGIFWithData:`方法将本地的gif图片转换为UIImage对象,并使用该UIImage对象创建一个`NSTextAttachment`对象。 4. 将NSTextAttachment对象添加到NSAttributedString中:将创建好的NSTextAttachment对象添加到NSMutableAttributedString对象中,可以使用`appendAttributedString:`方法将其追加到NSMutableAttributedString对象的末尾。 5. 将NSAttributedString对象显示在界面上:通过UILabel、UITextView等界面控件,将NSMutableAttributedString对象显示在界面上,即可完成富文本加载gif的功能。 需要注意的是,为了保证gif动图的流畅播放,SDWebImage库会将gif图片展示为一系列的静态图片,然后再按照正确的帧率进行播放。另外,对于高性能的gif加载,也可以使用其他优化库,如FLAnimatedImage等。 综上所述,通过引入SDWebImage库、下载gif图片、将图片添加到富文本中,并将富文本显示在界面上等一系列步骤,即可实现在iOS加载gif动图作为富文本显示的功能。 ### 回答3: 在iOS中,要加载GIF图像并将其显示在富文本中,我们可以采取以下步骤: 1. 首先,我们需要获取GIF图像文件的URL或路径。可以从互联网上下载或从应用程序的资源文件中获取。例如,如果GIF图像保存在应用程序的资源文件中,则可以使用`Bundle.main.url(forResource: "myGif", withExtension: "gif")`方法获取该文件的URL。 2. 接下来,我们需要将GIF图像文件加载到`Data`对象中,以便将其与`NSAttributedString`富文本一起使用。我们可以使用`Data(contentsOf: gifURL)`方法将URL转换为Data对象。 3. 然后,我们可以创建一个`NSTextAttachment`对象,并将GIF图像数据设置为其`image`属性。例如,可以使用`NSTextAttachment(image: UIImage(data: gifData)!)`来创建`NSTextAttachment`对象。 4. 然后,我们可以使用`NSAttributedString`的`append`方法将`NSTextAttachment`对象添加到富文本中,并设置其适当的位置。例如,可以使用`attributedString.append(NSAttributedString(attachment: textAttachment))`语句将`NSTextAttachment`对象添加到`attributedString`富文本字符串中。 5. 最后,我们可以将包含GIF图像的富文本字符串应用于文本视图或标签等UI元素,以便在界面上显示GIF图像。例如,对于UILabel,可以使用`label.attributedText = attributedString`将富文本字符串应用于标签。 综上所述,以上是利用iOS富文本加载GIF的简要步骤。通过将GIF图像文件加载到`Data`对象中,然后将其作为`NSTextAttachment`对象添加到富文本字符串中,我们可以在富文本中显示GIF图像,并将其应用于iOS界面中的相应UI元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值