Aar*_*ron 5 animation core-animation calayer ios careplicatorlayer
我正在使用CALayer,CAReplicatorLayer并将CABasicAnimation条形图设置为动画.条形图由向上的"段"组成,我将其呈现为带有的小矩形CALayer.然后我将它们作为子层添加到CAReplicatorLayer循环中.最后,我为每个小矩形添加一个动画,将矩形放入图表中的位置.
这是动画的最终状态:

这是动画中间的样子:

以下是实现它的代码:
#define BLOCK_HEIGHT 6.0f
#define BLOCK_WIDTH 8.0f
@interface TCViewController ()
@property (nonatomic, strong) NSMutableArray * blockLayers; // blocks that fall from the sky
@property (nonatomic, strong) NSMutableArray * replicatorLayers; // replicator layers that will replicate the blocks into position
@end
@implementation TCViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
self.blockLayers = [NSMutableArray array];
self.replicatorLayers = [NSMutableArray array];
NSArray * dataBlocksData = @[@1,@2,@3,@1,@5,@2,@11,@14,@5,@12,@10,@3,@7,@6,@3,@4,@1];
__block CGFloat currentXPosition = 0.0f;
[dataBlocksData enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL *stop) {
currentXPosition += BLOCK_WIDTH + 2.0f;
if (obj.integerValue == 0) return;
// replicator layer for data blocks in a column
CAReplicatorLayer * replicatorLayer = [[CAReplicatorLayer alloc] init];
replicatorLayer.frame = CGRectMake(currentXPosition, 0.0f, BLOCK_WIDTH, 200.0f);
// single data block layer (must be new for each replicator layer. can't reuse existing layer)
CALayer * blockLayer = [[CALayer alloc] init];
blockLayer.frame = CGRectMake(0, 200-BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
blockLayer.backgroundColor = [UIColor whiteColor].CGColor;
[replicatorLayer addSublayer:blockLayer];
replicatorLayer.instanceCount = obj.integerValue;
replicatorLayer.instanceDelay = 0.1f;
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, -BLOCK_HEIGHT, 0); // negative height moves back up the column
[self.blockLayers addObject:blockLayer];
[self.replicatorLayers addObject:replicatorLayer];
}];
}
- (void)viewDidLayoutSubviews {
// add replicator layers as sublayers
[self.replicatorLayers enumerateObjectsUsingBlock:^(CAReplicatorLayer * obj, NSUInteger idx, BOOL *stop) {
[self.view.layer addSublayer:obj];
}];
}
- (void)viewDidAppear:(BOOL)animated {
// add animations to each block layer
[self.blockLayers enumerateObjectsUsingBlock:^(CALayer * obj, NSUInteger idx, BOOL *stop) {
// position animation
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
animation.fromValue = @(-300);
animation.toValue = @(0);
animation.duration = 1.0f;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
[obj addAnimation:animation forKey:@"falling-block-animation"];
}];
}
Run Code Online (Sandbox Code Playgroud)
最后,这是抓住了.如果指定no timingFunction,Linear函数或EaseIn函数,则动画无法完全完成.像块一样不会完全落到位.看起来他们接近完成但不完全.只需一两帧.交换计时功能分配-viewDidAppear:,你会得到这种古怪:

来吧,试一试.我甚至尝试使用控制点(链接)指定一个功能,它与函数完全相同EaseIn:
// use control points for an EaseIn timing:
animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.42 :0.00 :1.0 :1.0];
// or simply specify the ease in timing function:
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
Run Code Online (Sandbox Code Playgroud)
由于某些原因EaseOut和Default计时功能工作正常,但在视觉上,一个简单的风格功能似乎需要更长的时间才能完成,也许是因为插值曲线的长度?
我已经尝试了很多东西来缩小这个问题:
-viewDidAppear.如果在视图的生命周期中更早或更晚添加动画,似乎没有什么区别.我真的想使用一个EaseIn计时功能,因为它是最好看的动画,用于落入到位的东西.我也想使用CAReplicatorLayer它,因为它完全符合我的要求.
那么EaseIn我正在使用它们的时序功能有什么问题?这是一个错误吗?
我同意这似乎是 CAReplicatorLayer 和 kCAMediaTimingFunctionEaseIn 计时函数组合中的一个错误。
作为解决方法,我建议使用具有三个关键帧的关键帧动画。前两个关键帧通过缓入时间定义您想要的动画。最后一个关键帧被定义为与第二个关键帧相同,但为动画提供了额外的时间来位于最终关键帧上,这似乎让复制器层将最后一个复制层动画化为最终状态。
因此我的建议是:
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
// extra keyframe duplicating final state
animation.values = @[@-300.0f, @0.0f, @0.0f];
// double length of original animation, with last half containing no actual animation
animation.keyTimes = @[@0.0, @0.5, @1.0];
animation.duration = 2.0f;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
// ease-in for first half of animation; default after
animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
[obj addAnimation:animation forKey:@"falling-block-animation"];
Run Code Online (Sandbox Code Playgroud)