是否可以将CALayer
的内容设置为动画GIF并让它显示该动画?我知道我可以将内容设置为这样的图像:
CALayer* subLayer = [CALayer layer];
NSImage *image = [[NSImage alloc] initWithData:data];
subLayer.contents = image;
图像将显示,但如果动画,则不会显示动画。获取GIF的单个帧是唯一的解决方案,获取帧速率,然后根据帧速率更改子层的content
?或者有一种我更容易忽视的方法吗?
答案 0 :(得分:5)
Swift 3 版本,但已更改为接收网址(出于我自己的目的)。
func createGIFAnimation(url:URL) -> CAKeyframeAnimation?{
guard let src = CGImageSourceCreateWithURL(url as CFURL, nil) else { return nil }
let frameCount = CGImageSourceGetCount(src)
// Total loop time
var time : Float = 0
// Arrays
var framesArray = [AnyObject]()
var tempTimesArray = [NSNumber]()
// Loop
for i in 0..<frameCount {
// Frame default duration
var frameDuration : Float = 0.1;
let cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(src, i, nil)
guard let framePrpoerties = cfFrameProperties as? [String:AnyObject] else {return nil}
guard let gifProperties = framePrpoerties[kCGImagePropertyGIFDictionary as String] as? [String:AnyObject]
else { return nil }
// Use kCGImagePropertyGIFUnclampedDelayTime or kCGImagePropertyGIFDelayTime
if let delayTimeUnclampedProp = gifProperties[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber {
frameDuration = delayTimeUnclampedProp.floatValue
}
else{
if let delayTimeProp = gifProperties[kCGImagePropertyGIFDelayTime as String] as? NSNumber {
frameDuration = delayTimeProp.floatValue
}
}
// Make sure its not too small
if frameDuration < 0.011 {
frameDuration = 0.100;
}
// Add frame to array of frames
if let frame = CGImageSourceCreateImageAtIndex(src, i, nil) {
tempTimesArray.append(NSNumber(value: frameDuration))
framesArray.append(frame)
}
// Compile total loop time
time = time + frameDuration
}
var timesArray = [NSNumber]()
var base : Float = 0
for duration in tempTimesArray {
timesArray.append(NSNumber(value: base))
base.add( duration.floatValue / time )
}
// From documentation of 'CAKeyframeAnimation':
// the first value in the array must be 0.0 and the last value must be 1.0.
// The array should have one more entry than appears in the values array.
// For example, if there are two values, there should be three key times.
timesArray.append(NSNumber(value: 1.0))
// Create animation
let animation = CAKeyframeAnimation(keyPath: "contents")
animation.beginTime = AVCoreAnimationBeginTimeAtZero
animation.duration = CFTimeInterval(time)
animation.repeatCount = Float.greatestFiniteMagnitude;
animation.isRemovedOnCompletion = false
animation.fillMode = kCAFillModeForwards
animation.values = framesArray
animation.keyTimes = timesArray
//animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.calculationMode = kCAAnimationDiscrete
return animation;
}
重要的是写在'CAKeyframeAnimation'的文档中:
数组中的第一个值必须为0.0,最后一个值必须为1.0。 该数组应该比values数组中显示的条目多一个。 例如,如果有两个值,则应该有三个关键时间。
所以添加了这一行:
timesArray.append(NSNumber(value: 1.0))
还检查了使用AVAssetExportSession导出为视频。
请这样打电话:
if let url = Bundle.main.url(forResource: "animation", withExtension: "gif"){
let animation = createGIFAnimation(url: url)
}
答案 1 :(得分:4)
好吧,好吧,我想这并不难做到。基本上我检查图像是否是GIF:
if ([format isEqualToString:@"gif"]){
CAKeyframeAnimation *animation = [self createGIFAnimation:data];
[subLayer addAnimation:animation forKey:@"contents"];
}
正如您所看到的,我正在调用我的自定义createGIFAnimation
方法,如下所示:
- (CAKeyframeAnimation *)createGIFAnimation:(NSData *)data{
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:data];
CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)(data), nil);
NSNumber *frameCount = [rep valueForProperty:@"NSImageFrameCount"];
// Total loop time
float time = 0;
// Arrays
NSMutableArray *framesArray = [NSMutableArray array];
NSMutableArray *tempTimesArray = [NSMutableArray array];
// Loop
for (int i = 0; i < frameCount.intValue; i++){
// Frame default duration
float frameDuration = 0.1f;
// Frame duration
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(src,i,nil);
NSDictionary *frameProperties = (__bridge NSDictionary*)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString*)kCGImagePropertyGIFDictionary];
// Use kCGImagePropertyGIFUnclampedDelayTime or kCGImagePropertyGIFDelayTime
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString*)kCGImagePropertyGIFUnclampedDelayTime];
if(delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
} else {
NSNumber *delayTimeProp = gifProperties[(NSString*)kCGImagePropertyGIFDelayTime];
if(delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
// Make sure its not too small
if (frameDuration < 0.011f){
frameDuration = 0.100f;
}
[tempTimesArray addObject:[NSNumber numberWithFloat:frameDuration]];
// Release
CFRelease(cfFrameProperties);
// Add frame to array of frames
CGImageRef frame = CGImageSourceCreateImageAtIndex(src, i, nil);
[framesArray addObject:(__bridge id)(frame)];
// Compile total loop time
time = time + frameDuration;
}
NSMutableArray *timesArray = [NSMutableArray array];
float base = 0;
for (NSNumber* duration in tempTimesArray){
//duration = [NSNumber numberWithFloat:(duration.floatValue/time) + base];
base = base + (duration.floatValue/time);
[timesArray addObject:[NSNumber numberWithFloat:base]];
}
// Create animation
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
animation.duration = time;
animation.repeatCount = HUGE_VALF;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.values = framesArray;
animation.keyTimes = timesArray;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.calculationMode = kCAAnimationDiscrete;
return animation;
}
非常简单,虽然我会喜欢有关如何改进它的一些提示。
答案 2 :(得分:1)
- (CAKeyframeAnimation *)createGIFAnimation:(NSData *)data{
CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)(data), nil);
int frameCount =(int) CGImageSourceGetCount(src);
// Total loop time
float time = 0;
// Arrays
NSMutableArray *framesArray = [NSMutableArray array];
NSMutableArray *tempTimesArray = [NSMutableArray array];
// Loop
for (int i = 0; i < frameCount; i++){
// Frame default duration
float frameDuration = 0.1f;
// Frame duration
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(src,i,nil);
NSDictionary *frameProperties = (__bridge NSDictionary*)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString*)kCGImagePropertyGIFDictionary];
// Use kCGImagePropertyGIFUnclampedDelayTime or kCGImagePropertyGIFDelayTime
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString*)kCGImagePropertyGIFUnclampedDelayTime];
if(delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
} else {
NSNumber *delayTimeProp = gifProperties[(NSString*)kCGImagePropertyGIFDelayTime];
if(delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
// Make sure its not too small
if (frameDuration < 0.011f){
frameDuration = 0.100f;
}
[tempTimesArray addObject:[NSNumber numberWithFloat:frameDuration]];
// Release
CFRelease(cfFrameProperties);
// Add frame to array of frames
CGImageRef frame = CGImageSourceCreateImageAtIndex(src, i, nil);
[framesArray addObject:(__bridge id)(frame)];
// Compile total loop time
time = time + frameDuration;
}
NSMutableArray *timesArray = [NSMutableArray array];
float base = 0;
for (NSNumber* duration in tempTimesArray){
//duration = [NSNumber numberWithFloat:(duration.floatValue/time) + base];
base = base + (duration.floatValue/time);
[timesArray addObject:[NSNumber numberWithFloat:base]];
}
// Create animation
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
animation.duration = time;
animation.repeatCount = HUGE_VALF;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.values = framesArray;
animation.keyTimes = timesArray;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.calculationMode = kCAAnimationDiscrete;
return animation;
}
根据mahal应该做的