我正在使用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"];
}];
}
最后,这是要抓住的地方。如果指定no timingFunction
,则一个Linear
或EaseIn
多个动画不会完全完成。就像块没有完全落入位。看来他们快要完成了,但还差得很远。只需关闭一两帧即可。交换定时功能分配-viewDidAppear:
,您将得到以下怪异:
继续尝试一下。我什至尝试通过控制点(link)指定一个功能,该功能与功能完全相同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];
出于某种原因EaseOut
,Default
定时功能可以正常工作,但样式上的简便似乎需要更长的时间才能完成(在视觉上),这也许是因为插值曲线的长度吗?
我已经尝试了许多方法来缩小问题的范围:
-viewDidAppear
. Didn't seem to make a difference if the animations were added earlier or later in the view's life cycle.I really want to use an EaseIn
timing function because its the best looking animation for something falling into place. I also want to use the CAReplicatorLayer
because it does exactly what I want to do.
So what's wrong with the EaseIn
timing functions as I'm using them? Is this a bug?
I agree that this appears to be a bug in the combination of CAReplicatorLayer and the kCAMediaTimingFunctionEaseIn timing function.
As a workaround, I suggest using a keyframe animation with three keyframes. The first two keyframes define your desired animation with the ease-in timing. The last keyframe is defined to be identical to the second keyframe, but gives the animation additional time to sit on the final keyframe, which seems to let the replicator layer animate that last replicated layer into the final state.
因此,我的建议是:
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"];
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句