UILabel上添加gif动态图

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


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

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

-(void)getImagesAndDuration{

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

       NSData*data = [NSDatadataWithContentsOfFile:path];

        CGImageSourceRefsource =CGImageSourceCreateWithData((__bridgeCFDataRef)data, NULL);

        size_tcount =CGImageSourceGetCount(source);

       _images= [NSMutableArrayarray];

       _duration=0.0f;

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

           CGImageRefimage =CGImageSourceCreateImageAtIndex(source, i,NULL);

            _duration+= [selfsd_frameDurationAtIndex:isource:source];

            [_imagesaddObject:[UIImageimageWithCGImage:imagescale:[UIScreenmainScreen].scaleorientation:UIImageOrientationUp]];

            CGImageRelease(image);

        }

}


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

   floatframeDuration =0.1f;

   CFDictionaryRefcfFrameProperties =CGImageSourceCopyPropertiesAtIndex(source, index,nil);

   NSDictionary*frameProperties = (__bridgeNSDictionary*)cfFrameProperties;

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

    

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

   if(delayTimeUnclampedProp) {

        frameDuration = [delayTimeUnclampedPropfloatValue];

    }

   else{

        

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

       if(delayTimeProp) {

            frameDuration = [delayTimePropfloatValue];

        }

    }

    

   // 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);

   returnframeDuration;

}


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

- (void)drawStrike:(CTFrameRef)frame

            inRect:(CGRect)rect

           context:(CGContextRef)c

{

    [superdrawStrike:frame inRect:rectcontext:c];

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

   CGFloatemojiWith =self.font.lineHeight*kEmojiWidthRatioWithLineHeight;

   CGFloatemojiOriginYOffset =self.font.lineHeight*kEmojiOriginYOffsetRatioWithLineHeight;

    

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

   CGFloatflushFactor =TTTFlushFactorForTextAlignment(self.textAlignment);

    

   CFArrayReflines =CTFrameGetLines(frame);

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

   CGPointlineOrigins[numberOfLines];

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

    

   BOOLtruncateLastLine = (self.lineBreakMode==NSLineBreakByTruncatingHead||self.lineBreakMode==NSLineBreakByTruncatingMiddle||self.lineBreakMode==NSLineBreakByTruncatingTail);

   CFRangetextRange =CFRangeMake(0, (CFIndex)[self.attributedTextlength]);

    

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

       CTLineRefline =CFArrayGetValueAtIndex(lines, lineIndex);

        

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

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

        

       CFIndextruncationAttributePosition = -1;

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

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

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

           CFRangelastLineRange =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;

               NSLineBreakModelineBreakMode =self.lineBreakMode;

                

               // Multiple lines, only use UILineBreakModeTailTruncation

               if(numberOfLines !=1) {

                    lineBreakMode =NSLineBreakByTruncatingTail;

                }

                

               switch(lineBreakMode) {

                   caseNSLineBreakByTruncatingHead:

                       break;

                   caseNSLineBreakByTruncatingMiddle:

                        truncationAttributePosition += (lastLineRange.length/2);

                       break;

                   caseNSLineBreakByTruncatingTail:

                   default:

                        truncationAttributePosition += (lastLineRange.length-1);

                       break;

                }

                

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

            }

        }

        

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

       for(idglyphRunin(__bridgeNSArray*)CTLineGetGlyphRuns(line)) {

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

           NSDictionary*attributes = (__bridgeNSDictionary*)CTRunGetAttributes((__bridgeCTRunRef) glyphRun);

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

           NSString*imageName = attributes[kCustomGlyphAttributeImageName];

           if(imageName) {

               CFRangeglyphRange =CTRunGetStringRange((__bridgeCTRunRef)glyphRun);

               if(glyphRange.location== truncationAttributePosition) {

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

                   continue;

                }

                

               CGRectrunBounds =CGRectZero;

               CGFloatrunAscent =0.0f;

               CGFloatrunDescent =0.0f;

                

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

                

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

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

                   continue;

                }

                

                runBounds.size.height= runAscent + runDescent;

                

               CGFloatxOffset =0.0f;

               switch(CTRunGetStatus((__bridgeCTRunRef)glyphRun)) {

                   casekCTRunStatusRightToLeft:

                        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 = [NSUserDefaultsstandardUserDefaults];

                   intimgIndex = [[nextImageobjectForKey:@"location"]intValue];

                    

                   UIImage*image;

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

                        imgIndex++;

                    }else{

                        imgIndex=0;

                    }

                    image = [self.rankGifImageArrayobjectAtIndex: imgIndex];


                    [nextImagesetObject:[NSStringstringWithFormat:@"%d",imgIndex]forKey:@"location"];


                    

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

                    runBounds.size.width=28;

                    runBounds.size.height=13;

                   CGContextDrawImage(c, runBounds, image.CGImage);

                    

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

}


- (void)startGif:(NSTimeInterval)time

{

   if(!gifTimer) {

        gifTimer= [NSTimertimerWithTimeInterval:timetarget:selfselector:@selector(gifAnimate:)userInfo:nilrepeats:YES];

        [[NSRunLoopmainRunLoop]addTimer:gifTimerforMode:NSRunLoopCommonModes];

    }

   

}


- (void)gifAnimate:(NSTimer*)timer

{

    [supersetNeedsDisplay];

}


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






版权声明:本文为codeWorldPeace原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。