美文网首页iOS Dev
YYImage之YYAnimatedImageView

YYImage之YYAnimatedImageView

作者: MaZengyi | 来源:发表于2017-01-08 02:08 被阅读339次

    本文是 YYImage 的最后一篇。主要探究一下 YYAnimatedImageView 如何显示一个动画图片,将详细展示一个动画的图片如 Gif,是如何一步步展示出来的。

    如何展示一个动画图片

    如果你是使用 UIImageView,那么你可以设置 animationImages 属性,之后可以调用 startAnimating 方法来进行播放一组的图片组成的动画,调用 stopAnimating 可以停止当前播放的动画。YYAnimatedImageView 继承与 UIImageView,所以也可以使用上述的方法来完成动画的播放,如果你使用了遵守 YYAnimatedImage 协议的图片对象如 YYImage,YYFrameImage, YYSpriteSheetImage,则会调用 YYAnimatedImageView 内部封装的方法来进行动画的展示,当然,这样的可控性也是最高的。

    播放动画帧

    在设置了 YYAnimatedImageView 的 Image(使用遵守 YYAnimatedImage 协议的图片对象) 之后,会调用内部的

    - (void)setImage:(id)image withType:(YYAnimatedImageType)type 
    

    来进行播放前的一些准备

    1. 会重置当前的播放状态,生成 CADisplayLink 来进行播放动画帧,不立即启动,CADisplayLink 的 pause 设置为 YES。
    2. 清空之前缓存图片 buff

    完成之后,开始播放的操作,将 CADisplayLink 的 pause 设置为 NO,在 CADisplayLink 的 1/60 秒的间隔下调用 step 方法来展示一张张的图片。

    step 做了什么

    在 CADisplayLink 周期内,step 会被不断的调用,它的主要功能是将每一帧的动画取出,并且展示。我们来一起看看如何来处理,由于代码比较长,总结出来之后的流程如下

    首先 判断所有的动画帧 是否取出,如果没取出则会去取出每一帧的图片,并且存入用字典创建的 buff 中。

    异步的处理图片数据

    YYAnimatedImageView 使用 NSOperationQueue 来进行每一帧图片的提取,因为 UIImage 是线程安全,可以在后台线程中获取之后存入 buff 中,不占用主线程,增强播放性能,代码如下

    @implementation _YYAnimatedImageViewFetchOperation
    - (void)main {
        __strong YYAnimatedImageView *view = _view;
        if (!view) return;
        if ([self isCancelled]) return;
        view->_incrBufferCount++;
        if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
        if (view->_incrBufferCount > (NSInteger)view->_maxBufferCount) {
            view->_incrBufferCount = view->_maxBufferCount;
        }
        NSUInteger idx = _nextIndex;
        NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
        NSUInteger total = view->_totalFrameCount;
        view = nil;
        
        for (int i = 0; i < max; i++, idx++) {
            @autoreleasepool {
                if (idx >= total) idx = 0;
                if ([self isCancelled]) break;
                __strong YYAnimatedImageView *view = _view;
                if (!view) break;
                LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
                
                if (miss) {
                    UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
                    img = img.yy_imageByDecoded;
                    if ([self isCancelled]) break;
                    LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
                    view = nil;
                }
            }
        }
    }
    @end
    

    buff 提取完成之后就可以正式的开始展示了,首先取出每一帧的播放时长 duration,于当前的 CADisplayLink 的周期进行判断,在合适的时间切换图片。

    切换图片

    在可以切换的时间时,会调用

     [self.layer setNeedsDisplay]; 
    

    来让 YYAnimatedImageView 的 layer 进行重绘,使得系统回调 displayLayer 来完成 layer content 的设置

    - (void)displayLayer:(CALayer *)layer {
        if (_curFrame) {
            layer.contents = (__bridge id)_curFrame.CGImage;
        }
    }
    

    在 CADisplayLink 的不断回调之下,完成一套图片的展示

    细节末节

    • YYAnimatedImageView 会监听 UIApplicationDidReceiveMemoryWarningNotification 来在内存紧张的时候释放 buff

    • YYAnimatedImageView 会监听 UIApplicationDidEnterBackgroundNotification 在 APP 进入后台的时候来暂停动画,节省系统开销

    • YYAnimatedImageView 会根据当前的手机的内存情况动态的优化 buff 使用的大小,保证不会超过内存占用的 20% 和剩余内存的 60%,算法如下

    // dynamically adjust buffer size for current memory.
    - (void)calcMaxBufferCount {
        int64_t bytes = (int64_t)_curAnimatedImage.animatedImageBytesPerFrame;
        if (bytes == 0) bytes = 1024;
        
        int64_t total = _YYDeviceMemoryTotal();
        int64_t free = _YYDeviceMemoryFree();
        int64_t max = MIN(total * 0.2, free * 0.6);
        max = MAX(max, BUFFER_SIZE);
        if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
        double maxBufferCount = (double)max / (double)bytes;
        if (maxBufferCount < 1) maxBufferCount = 1;
        else if (maxBufferCount > 512) maxBufferCount = 512;
        _maxBufferCount = maxBufferCount;
    }
    

    总结

    YYAnimatedImageView 使用 CADisplayLink 来完成动画帧的播放,并且异步的获取图片资源并解码,还支持动态的调整内存占用率,是一个高性能的图片框架

    相关文章

      网友评论

        本文标题:YYImage之YYAnimatedImageView

        本文链接:https://www.haomeiwen.com/subject/yikqbttx.html