UILabel上添加gif动态图

遇到一个新需求,需要在富文本上添加gif或者其他动态图,原来的使用的富文本不支持动态图,就研究下了国外大牛的demo,其实还是很简单的,主要原理就是把动态图切成一帧帧图片,然后利用NSTimer和动态图的周期循环在UILabel上画图片。。。但是亲测了一下不会引起cpu和内存方面的担忧,所以我们可以在第三方的富文本源码上进行修改或者重新封装一下,废话不多少,直接上代码。


1.拿gif为例,首先处理gif成图片数字和得到动画周期

 //测试 处理消息区 gif  得到 图片数组和动画周期 _images _duration

-(void)getImagesAndDuration{

       NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"gif"];

        NSData *data = [NSData dataWithContentsOfFile:path];

        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

        size_t count = CGImageSourceGetCount(source);

        _images = [NSMutableArray array];

        _duration = 0.0f;

        for (size_t i = 0; i < count; i++) {

            CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);

            _duration += [self sd_frameDurationAtIndex:i source:source];

            [_images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];

            CGImageRelease(image);

        }

}


-(float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {

    float frameDuration = 0.1f;

    CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);

    NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;

    NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];

    

    NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];

    if (delayTimeUnclampedProp) {

        frameDuration = [delayTimeUnclampedProp floatValue];

    }

    else {

        

        NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];

        if (delayTimeProp) {

            frameDuration = [delayTimeProp floatValue];

        }

    }

    

    // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.

    // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify

    // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>

    // for more information.

    

    if (frameDuration < 0.011f) {

        frameDuration = 0.100f;

    }

    CFRelease(cfFrameProperties);

    return frameDuration;

}


2.绘制gif 我们的项目用的是TTTAttributedLabel,绘制图片代码基本都差不多,根据自己项目的需要,传入相应的数据,在绘制图片处进行修改

- (void)drawStrike:(CTFrameRef)frame

            inRect:(CGRect)rect

           context:(CGContextRef)c

{

    [super drawStrike:frame inRect:rect context:c];

    //PS:这个是在TTTdrawFramesetter....方法最后做了修改的基础上。

    CGFloat emojiWith = self.font.lineHeight*kEmojiWidthRatioWithLineHeight;

    CGFloat emojiOriginYOffset = self.font.lineHeight*kEmojiOriginYOffsetRatioWithLineHeight;

    

    //修正绘制offset,根据当前设置的textAlignment

    CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment);

    

    CFArrayRef lines = CTFrameGetLines(frame);

    NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines);

    CGPoint lineOrigins[numberOfLines];

    CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);

    

    BOOL truncateLastLine = (self.lineBreakMode == NSLineBreakByTruncatingHead || self.lineBreakMode == NSLineBreakByTruncatingMiddle || self.lineBreakMode == NSLineBreakByTruncatingTail);

    CFRange textRange = CFRangeMake(0, (CFIndex)[self.attributedText length]);

    

    for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {

        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);

        

        //这里其实是能获取到当前行的真实origin.x,根据textAlignment,而lineBounds.origin.x其实是默认一直为0(不会受textAlignment影响)

        CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, rect.size.width);

        

        CFIndex truncationAttributePosition = -1;

        //检测如果是最后一行,是否有替换...

        if (lineIndex == numberOfLines - 1 && truncateLastLine) {

            // Check if the range of text in the last line reaches the end of the full attributed string

            CFRange lastLineRange = CTLineGetStringRange(line);

            

            if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) {

                // Get correct truncationType and attribute position

                truncationAttributePosition = lastLineRange.location;

                NSLineBreakMode lineBreakMode = self.lineBreakMode;

                

                // Multiple lines, only use UILineBreakModeTailTruncation

                if (numberOfLines != 1) {

                    lineBreakMode = NSLineBreakByTruncatingTail;

                }

                

                switch (lineBreakMode) {

                    case NSLineBreakByTruncatingHead:

                        break;

                    case NSLineBreakByTruncatingMiddle:

                        truncationAttributePosition += (lastLineRange.length / 2);

                        break;

                    case NSLineBreakByTruncatingTail:

                    default:

                        truncationAttributePosition += (lastLineRange.length - 1);

                        break;

                }

                

                //如果要在truncationAttributePosition这个位置画表情需要忽略

            }

        }

        

        //找到当前行的每一个要素,姑且这么叫吧。可以理解为有单独的attr属性的各个range

        for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns(line)) {

            //找到此要素所对应的属性

            NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun);

            //判断是否有图像,如果有就绘制上去

            NSString *imageName = attributes[kCustomGlyphAttributeImageName];

            if (imageName) {

                CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun);

                if (glyphRange.location == truncationAttributePosition) {

                    //这里因为glyphRangelength肯定为1,所以只做这一个判断足够

                    continue;

                }

                

                CGRect runBounds = CGRectZero;

                CGFloat runAscent = 0.0f;

                CGFloat runDescent = 0.0f;

                

                runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL);

                

                if (runBounds.size.width!=emojiWith) {

                    //这一句是为了在某些情况下,例如单行省略号模式下,默认行为会将个别表情的runDelegate改变,也就改变了其大小。这时候会引起界面上错乱,这里做下检测(浮点数做等于判断似乎有点操蛋啊。。)

                    continue;

                }

                

                runBounds.size.height = runAscent + runDescent;

                

                CGFloat xOffset = 0.0f;

                switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) {

                    case kCTRunStatusRightToLeft:

                        xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location + glyphRange.length, NULL);

                        break;

                    default:

                        xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location, NULL);

                        break;

                }

                runBounds.origin.x = penOffset + xOffset;

                runBounds.origin.y = lineOrigins[lineIndex].y;

                runBounds.origin.y -= runDescent;



//上面代码千篇一律 重点在这里 self.rankGifImageArray self.duration 是图片数组和动画周期

      NSUserDefaults * nextImage = [NSUserDefaults standardUserDefaults];

                    int imgIndex = [[nextImage objectForKey:@"location"] intValue];

                    

                    UIImage *image;

                    if (imgIndex < [self.rankGifImageArray count]-1) {

                        imgIndex++;

                    }else{

                        imgIndex= 0;

                    }

                    image = [self.rankGifImageArray objectAtIndex: imgIndex];


                    [nextImage setObject:[NSString stringWithFormat:@"%d",imgIndex] forKey: @"location"];


                    

                    runBounds.origin.y += 3; //稍微矫正下。

                    runBounds.size.width = 28;

                    runBounds.size.height = 13;

                    CGContextDrawImage(c, runBounds, image.CGImage);

                    

                    [self startGif:self.duration/self.rankGifImageArray.count];

}


- (void)startGif:(NSTimeInterval)time

{

    if (!gifTimer) {

         gifTimer = [NSTimer timerWithTimeInterval:time target:self selector:@selector(gifAnimate:) userInfo:nil repeats:YES];

        [[NSRunLoop mainRunLoop] addTimer:gifTimer forMode:NSRunLoopCommonModes];

    }

   

}


- (void)gifAnimate:(NSTimer *)timer

{

    [super setNeedsDisplay];

}


总结:我们要做的就是在富文本处理图片的基础上进行修改,达到实现动态图效果。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值