我尝试将CADisplayLink和CAAnimation结合起来,以便我可以使用CAAnimation为某些图层设置动画,并根据动画图层的更改表示层实时调整其他图层。我意识到CAAnimations正在我的进程之外被驱动,而CADisplayLink回调在我的进程中,所以同步永远不会完全紧张。但是,我希望它会比我看到的更好(我看到可能会达到4帧滞后)。
归结为一小段示例代码,我创建了一堆图层。其中一半是轮廓,另一半是实心方块 - 所有项目都有相同的大小。目标是为这些矩形对设置动画,使它们的轮廓和正方形始终在一起(即相同的框架)。
每个循环,我为实体图层设置一个新位置,让CADisplayLink回调根据其互补实体图层的表示层位置设置轮廓图层。在模拟器上,这似乎在大多数时间都有效,至少对于少量的层来说。一旦我达到24对图层,一些轮廓开始滞后。在设备(iPad Air和iPhone 6)上,即使只有一个正方形,轮廓也会滞后。我正在用iOs 8进行所有测试。
以下是我的ViewController实现。
@implementation ViewController
{
CADisplayLink *_displayLink;
NSUInteger _startingLayerIndex;
}
#define NUMLAYERS 1
- (void)viewDidLoad {
[super viewDidLoad];
_startingLayerIndex = self.view.layer.sublayers.count;
for (int i=0; i<NUMLAYERS; ++i) {
CALayer *outlineLayer = [[CALayer alloc] init];
outlineLayer.borderColor = [[UIColor blackColor] CGColor];
outlineLayer.borderWidth = 1.0;
CALayer *fillLayer = [[CALayer alloc] init];
fillLayer.backgroundColor = [[UIColor redColor] CGColor];
{
CGRect r = CGRectMake(self.view.center.x, self.view.center.y, 0, 0);
r = CGRectInset(r, -22, -22);
outlineLayer.frame = r;
fillLayer.frame = r;
}
[self.view.layer addSublayer:fillLayer];
[self.view.layer addSublayer:outlineLayer];
}
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
[self.view addGestureRecognizer:gr];
// WARNING - will create an ARC cycle. I don't care though, throwaway code
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(refreshDisplay:)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
_displayLink.paused = true;
}
-(void) refreshDisplay:(CADisplayLink *) displayLink {
[CATransaction begin];
[CATransaction setDisableActions:true];
bool keepGoing = false;
for (int i=0; i<NUMLAYERS; ++i) {
CALayer *animatedLayer = [self.view.layer.sublayers objectAtIndex:(2*i)+_startingLayerIndex];
if (animatedLayer.animationKeys.count) {
keepGoing = true;
}
CALayer *trackingLayer = [self.view.layer.sublayers objectAtIndex:(2*i)+_startingLayerIndex+1];
trackingLayer.position = [animatedLayer.presentationLayer position];
}
_displayLink.paused = !keepGoing;
[CATransaction commit];
}
CGFloat randInRange(CGFloat r) {
return (rand()%((int)r*2))/2.0;
}
-(void) onTap:(UITapGestureRecognizer *) gr {
_displayLink.paused = false;
[CATransaction begin];
[CATransaction setAnimationDuration:2.0];
//[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
for (int i=0; i<NUMLAYERS; ++i) {
CGSize sz = self.view.frame.size;
CGPoint p = CGPointMake(randInRange(sz.width), randInRange(sz.height));
[[self.view.layer.sublayers objectAtIndex:(2*i)+_startingLayerIndex] setPosition:p];
}
[CATransaction commit];
}
@end
我尝试了3种不同的计时功能,我看到了相同的结果。我还检查了仪器的帧速率,我得到58-60 fps,仍然看到可见的分离。
我想知道是否有人知道更好的方法。我知道我可以将所有动画转换为由CAAnimation或CADisplayLink驱动。但是,就我而言,两者都没有吸引力。我在屏幕上有数百个图层,所有图层都互相连接(构成一个始终响应和完全动画的视图) - 有时是分层次的。核心动画让生活变得更易于管理(而且我认为效率更高,尽管可能是效率&#34;这是我滞后的根本原因) - 所以我不想抛出那个。我不必为我的所有动画参数化而感到兴奋。有许多层依赖于许多其他层的帧。能够为大部分工作运行动画,然后只是做一些角落案例&#34;手工&#34;从设计角度(尤其是维护方面),实时非常吸引人。
另外,我意识到UIKit Dynamics的构建是为动画和响应式视图提供良好的基础。但是,我调整了图层的大小,屏幕上各层的位置需要约束和附件。将其扩展到数百甚至数千个层似乎存在问题,尤其是当CPU已经受到限制时。