美文网首页
YYAnimatedImageView

YYAnimatedImageView

作者: Code_人生 | 来源:发表于2019-09-30 14:01 被阅读0次

    一、源码分析

    1、来到YYAnimatedImageView.m- (instancetype)initWithImage:(UIImage *)image;

    - (instancetype)initWithImage:(UIImage *)image {
        self = [super init];
        _runloopMode = NSRunLoopCommonModes;
        _autoPlayAnimatedImage = YES;
        self.frame = (CGRect) {CGPointZero, image.size };
        //重写了set方法
        self.image = image;
        return self;
    }
    

    2、点击.image

    - (void)setImage:(UIImage *)image {
        if (self.image == image) return;
        [self setImage:image withType:YYAnimatedImageTypeImage];
    }
    

    3、点击setImage: withType:

    - (void)setImage:(id)image withType:(YYAnimatedImageType)type {
        [self stopAnimating];
        if (_link) [self resetAnimated];
        _curFrame = nil;
        switch (type) {
            case YYAnimatedImageTypeNone: break;
            case YYAnimatedImageTypeImage: super.image = image; break;
            case YYAnimatedImageTypeHighlightedImage: super.highlightedImage = image; break;
            case YYAnimatedImageTypeImages: super.animationImages = image; break;
            case YYAnimatedImageTypeHighlightedImages: super.highlightedAnimationImages = image; break;
        }
        [self imageChanged];
    }
    
    1. 1 点击imageChanged
    - (void)imageChanged {
        YYAnimatedImageType newType = [self currentImageType];
        id newVisibleImage = [self imageForType:newType];
        NSUInteger newImageFrameCount = 0;
        BOOL hasContentsRect = NO;
        if ([newVisibleImage isKindOfClass:[UIImage class]] &&
            [newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)]) {
            newImageFrameCount = ((UIImage<YYAnimatedImage> *) newVisibleImage).animatedImageFrameCount;
            if (newImageFrameCount > 1) {
                hasContentsRect = [((UIImage<YYAnimatedImage> *) newVisibleImage) respondsToSelector:@selector(animatedImageContentsRectAtIndex:)];
            }
        }
        if (!hasContentsRect && _curImageHasContentsRect) {
            if (!CGRectEqualToRect(self.layer.contentsRect, CGRectMake(0, 0, 1, 1)) ) {
                [CATransaction begin];
                [CATransaction setDisableActions:YES];
                self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
                [CATransaction commit];
            }
        }
        _curImageHasContentsRect = hasContentsRect;
        if (hasContentsRect) {
            CGRect rect = [((UIImage<YYAnimatedImage> *) newVisibleImage) animatedImageContentsRectAtIndex:0];
            [self setContentsRect:rect forImage:newVisibleImage];
        }
        
        if (newImageFrameCount > 1) {
            [self resetAnimated];
            _curAnimatedImage = newVisibleImage;
            _curFrame = newVisibleImage;
            _totalLoop = _curAnimatedImage.animatedImageLoopCount;
            _totalFrameCount = _curAnimatedImage.animatedImageFrameCount;
            [self calcMaxBufferCount];
        }
        [self setNeedsDisplay];
        [self didMoved];
    }
    
    - (void)didMoved {
        if (self.autoPlayAnimatedImage) {
            if(self.superview && self.window) {
                [self startAnimating];
            } else {
                [self stopAnimating];
            }
        }
    }
    

    4.2、点击resetAnimated

    - (void)resetAnimated {
        dispatch_once(&_onceToken, ^{
            _lock = dispatch_semaphore_create(1);
            _buffer = [NSMutableDictionary new];
            _requestQueue = [[NSOperationQueue alloc] init];
            _requestQueue.maxConcurrentOperationCount = 1;
            //CADisplayLink:基于屏幕刷新频率的; NSTimer(预先的时间点注册好了事件!)
            _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(step:)];
            if (_runloopMode) {
                [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
            }
            _link.paused = YES;
            
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        });
        
        [_requestQueue cancelAllOperations];
        LOCK(
             if (_buffer.count) {
                 //后台线程异步释放对象的做法
                 //对象的释放  dealloc
                 NSMutableDictionary *holder = _buffer;
                 //清除内容了!!!
                 _buffer = [NSMutableDictionary new];
                 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
                     // Capture the dictionary to global queue,
                     // release these images in background to avoid blocking UI thread.
                     [holder class];
                 });
             }
        );
        _link.paused = YES;
        _time = 0;
        if (_curIndex != 0) {
            [self willChangeValueForKey:@"currentAnimatedImageIndex"];
            _curIndex = 0;
            [self didChangeValueForKey:@"currentAnimatedImageIndex"];
        }
        _curAnimatedImage = nil;
        _curFrame = nil;
        _curLoop = 0;
        _totalLoop = 0;
        _totalFrameCount = 1;
        _loopEnd = NO;
        _bufferMiss = NO;
        _incrBufferCount = 0;
    }
    
    • 点击step
    //刷新频率有关
    - (void)step:(CADisplayLink *)link {
        UIImage <YYAnimatedImage> *image = _curAnimatedImage;
        NSMutableDictionary *buffer = _buffer;
        UIImage *bufferedImage = nil;
        NSUInteger nextIndex = (_curIndex + 1) % _totalFrameCount;
        BOOL bufferIsFull = NO;
        
        if (!image) return;
        if (_loopEnd) { // view will keep in last frame
            [self stopAnimating];
            return;
        }
        
        NSTimeInterval delay = 0;
        if (!_bufferMiss) {
            _time += link.duration;
            delay = [image animatedImageDurationAtIndex:_curIndex];
            if (_time < delay) return;
            _time -= delay;
            if (nextIndex == 0) {
                _curLoop++;
                if (_curLoop >= _totalLoop && _totalLoop != 0) {
                    _loopEnd = YES;
                    [self stopAnimating];
                    [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
                    return; // stop at last frame
                }
            }
            delay = [image animatedImageDurationAtIndex:nextIndex];
            if (_time > delay) _time = delay; // do not jump over frame
        }
        LOCK(
             bufferedImage = buffer[@(nextIndex)];
             if (bufferedImage) {
                 if ((int)_incrBufferCount < _totalFrameCount) {
                     [buffer removeObjectForKey:@(nextIndex)];
                 }
                 [self willChangeValueForKey:@"currentAnimatedImageIndex"];
                 _curIndex = nextIndex;
                 [self didChangeValueForKey:@"currentAnimatedImageIndex"];
                 _curFrame = bufferedImage == (id)[NSNull null] ? nil : bufferedImage;
                 if (_curImageHasContentsRect) {
                     _curContentsRect = [image animatedImageContentsRectAtIndex:_curIndex];
                     [self setContentsRect:_curContentsRect forImage:_curFrame];
                 }
                 nextIndex = (_curIndex + 1) % _totalFrameCount;
                 _bufferMiss = NO;
                 if (buffer.count == _totalFrameCount) {
                     bufferIsFull = YES;
                 }
             } else {
                 _bufferMiss = YES;
             }
        )//LOCK
        
        if (!_bufferMiss) {
            [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
        }
        
        if (!bufferIsFull && _requestQueue.operationCount == 0) { // if some work not finished, wait for next opportunity
            _YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
            operation.view = self;
            operation.nextIndex = nextIndex;
            operation.curImage = image;
            [_requestQueue addOperation:operation];
        }
    }
    

    相关文章

      网友评论

          本文标题:YYAnimatedImageView

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