在如今的项目中纯展示类的项目中如果没有做支持gif的,恐怕连一个demo都不算,更别说做成一个项目了。
现在对很多类库都有成熟的资源,最为常见的sdwebimage都有支持gif的展示问题。可是千变万化的需求不是有限的资源库所能够解决的,例如我们今天的要求,sdwebimage虽然可以展示,但是需要剪切固定frame就不是简单的一句代码可以解决的了。
要做成gif的动图展示需要将动图下载下来,然后将动图按帧取出来。每一帧包含一个image以及每一个image需要战士的time。这样的组合起来的动图就是最初的gif动图。好了,接下来讲讲如何做。
先从网上找来一张gif动图,static NSString *gifUrlStr =@"http://images11.app.happyjuzi.com/content/201607/13/5785f5636cf38.gif";
定义出来gif的路径用来下载动图。
然后声明需要展示动图的imageview。
self.originGif = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 260)];
_originGif.image = [UIImage imageWithData:gifData];
[self.view addSubview:_originGif];
上面的gifdata就是需要下载的gif资源了。这里为了展示,就不做代码优化(异步线程下载+硬盘以及内存缓存处理)了。
NSURL *gifUrl = [NSURL URLWithString:gifUrlStr];
NSData *gifData= [NSData dataWithContentsOfURL:gifUrl options:NSDataReadingMappedIfSafe error:nil];
//下面会单独出这个方法
[self decodeWithData:gifData];
在下载完动图以后我们需要拆分gif,这个时候就需要之前说的存放image和time的两个数组了。
self.imageSource = [NSMutableArray new];
self.delayTimeSource = [NSMutableArray new];
接下来才是最重要的,拆分gif
-(void)decodeWithData:(NSData *)data
{
CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef) data, NULL);
if (src)
{
//获取gif的帧数
NSUInteger frameCount = CGImageSourceGetCount(src);
//获取GfiImage的基本数据
NSDictionary *gifProperties = (NSDictionary *) CGImageSourceCopyProperties(src, NULL);
if(gifProperties)
{
//由GfiImage的基本数据获取gif数据
NSDictionary *gifDictionary =[gifProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary];
//获取gif的播放次数
_loopCount = [[gifDictionary objectForKey:(NSString*)kCGImagePropertyGIFLoopCount] integerValue];
for (NSUInteger i = 0; i < frameCount; i++)
{
//得到每一帧的CGImage
CGImageRef img = CGImageSourceCreateImageAtIndex(src, (size_t) i, NULL);
if (img)
{
//把CGImage转化为UIImage
UIImage *frameImage = [UIImage imageWithCGImage:img];
//在这里可以对单张imageframe做处理然后放到_imageSource数组里面,那么新的gif就会生成。
[_imageSource addObject:frameImage];
//获取每一帧的图片信息
NSDictionary *frameProperties = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(src, (size_t) i, NULL);
if (frameProperties)
{
//由每一帧的图片信息获取gif信息
NSDictionary *frameDictionary = [frameProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary];
//取出每一帧的delaytime
CGFloat delayTime = [[frameDictionary objectForKey:(NSString*)kCGImagePropertyGIFDelayTime] floatValue];
[_delayTimeSource addObject:[NSNumber numberWithFloat:delayTime]];
//TODO 这里可以实现边解码边回调播放或者把每一帧image和delayTime存储起来
CFRelease(frameProperties);
}
CGImageRelease(img);
}
}
CFRelease(gifProperties);
}
CFRelease(src);
//这个方法是处理展示类型的(例如是否循环,循环次数)
[self playImageWithIndex];
}
}
而在展示动图显示类型里面就用到了一个循环调用的方法。我这里写两种方式,一种可以取消但是必须在主线程操作,一种是使用GCD但是一旦开启就无法取消。
-(void)playImageWithIndex
{
CGFloat playOneTime = 0;
__block typeof (self) weakSelf = self;
playOneTime = [_delayTimeSource[0] floatValue];
// //GCD方式延迟调用(无法取消)
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(playOneTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// UIImage *image = _imageSource[index];
// [_originGif setImage:image];
//
// NSInteger newIndex = index;
// if (index<_imageSource.count-1) {
// newIndex++;
// }
// else
// {
// newIndex =0;
// }
// [weakSelf playImageWithIndex:newIndex];
// });
//nstimer的方式调用(可以取消,但必须在主线程调用)
[timer invalidate];
timer = nil;
timer = [NSTimer scheduledTimerWithTimeInterval:playOneTime target:weakSelf selector:@selector(playImageWithIndex) userInfo:nil repeats:NO];
}
以上就是循环调用展示动图,并将动图剪切之后再重新封装成需要的gif的简单代码。