我正在使用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:
,你会得到这个奇怪的东西:
来吧,试试看。我什至尝试使用与函数完全相同的控制点(链接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
计时功能可以正常工作,但在视觉上,轻松的风格功能似乎需要更长的时间才能完成,也许是因为插值曲线的长度?
我尝试了很多方法来缩小问题范围:
- 不循环这些结构,并且仅使用具有相同动画的 1 层和 1 复制器层。我的意思是,只为一列设置动画。没运气。循环不是问题。
- 将创建、添加子层和添加动画的代码全部放在
-viewDidAppear
. 如果动画是在视图生命周期的早期或晚期添加的,似乎并没有什么不同。 - 为我的结构使用属性。没有不同。
- 不为我的结构使用属性。再次,没有区别。
- 添加了动画完成的回调以删除动画,从而使列保持正确的“最终”状态。这不是很好,但值得一试。
- 这个问题在 iOS 6 和 7 中都存在。
我真的很想使用EaseIn
计时功能,因为它是放置到位的最佳动画。我也想使用,CAReplicatorLayer
因为它正是我想做的。
那么EaseIn
我使用的计时功能有什么问题呢?这是一个错误吗?