CALayer的CAKeyframeAnimation位于关键帧值的中间位置?

时间:2017-03-28 07:24:34

标签: ios objective-c calayer caanimation cakeyframeanimation

假设我想将layer从A点移动到B然后移动到C:

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];
animation.values = @[pointA, pointB, pointC];
animation.duration = 1;

将动画添加到图层后,我将其速度设置为0:

animation.speed = 0;

所以我可以使用滑块来调整图层的位置:

layer.timeOffset = slider.value;

但如果我的图层的当前位置位于pointB,则在添加动画后,它会移回pointA,即使我将layer的{​​{1}}设置为0.5。< / p>

我错过了什么吗?

谢谢!

更新: 为了更好地说明问题,这里有一些测试代码:

timeOffset

- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.testLayer = [CALayer layer]; self.testLayer.frame = CGRectMake(100, 100, 30, 30); self.testLayer.backgroundColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:self.testLayer]; } - (IBAction)action:(id)sender { CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.y"]; animation.values = @[@50, @100, @150]; animation.duration = 3.0f; [self.testLayer addAnimation:animation forKey:@"animation"]; self.testLayer.speed = 0; } 的Y位置为100,而添加的动画的键值为50,100和150,当添加动画时,testLayer将被移动到第一个键值位置,即50,但如何防止这种情况?我试图设置testLayer,但它没有帮助。

3 个答案:

答案 0 :(得分:0)

面纱背后有一些神秘的功能,经过我自己的一些实验后,看起来就像动画的动作与一段时间之后发出的调用不同的一样timeOffset setter的直接调用:(这是在Swift中,但是概念是一样的)

layer.add(animation, forKey: "...")
layer.timeOffset = CFTimeInterval(0.5)

以某种方式为图层下的所有动画设置了“全局”偏移量,如果以这种方式调用timeOffset setter,则所有动画在0.5秒后开始工作。

/* The animation won't do anything when slider value is 0 - 0.5,
 * even if the function call is exactly the same. It would perform
 * the animation at time offset 0 - 0.5 when the slider value is 
 * 0.6 - 1.0.
 */
layer.timeOffset = CFTimeInterval(slider.value)

为了达到你想要的效果,你可以这样做:

// Note: do not set timeOffset on the layer.
animation.timeOffset = CFTimeInterval(0.5)
layer.add(animation, forKey: "...")

但是,由于您的点ABC未必形成关闭路径,因此滑块值(因此timeOffset)超过{{ 1}},你的图层会立即移回0.5,如果滑块值小于A,图层就会消失,所以你必须这样做:

0

这使得动画可以根据需要正确执行,但必须有一些更好的方法,我也很好奇为什么// set the initial slider value at 0.5 slider.value = 0.5 // match the slider value to actual animation time offset layer.timeOffset = CFTimeInterval(slider.value > 0.5 ? slider.value - 0.5 : slider.value + 0.5) setter采取了不同的行为,希望有人擅长timeOffset将能够解释。

答案 1 :(得分:0)

老实说,这是最棘手的问题之一。不可否认,CALayers和CAAnimation的时机一般非常复杂。通常,在添加动画时更新模型值是好的,但是您希望通过百分比/滑块来完成,所以这是我完全理解的挑战。 Cytus的答案非常接近,因为在添加新动画时设置layer.timeOffset时似乎存在一个错误。见When is layer.timeOffset Available。设置动画本身的timeOffset是有效的,但滑块处于最大值时删除了值。如果在viewDidAppear中添加动画并且仅在滑块操作方法中更新timeOffset,它也可以完美地工作。我开始记录图层时间值,并注意到beginTime的一些疯狂的东西。简短的回答是当您添加新动画时,您必须将beginTime(特别是在这种情况下)设置为CACurrentMediaTime()并且它们都能正常工作。很奇怪,因为通常我会将它用于延迟,但是反复替换当前动画的东西会产生对我一直认为是可选的这个属性的需求。我会说你完成后应该删除动画以防止表示层被污染,但这取决于你。这是一个工作示例,它将动画保持在原位,而不会出现故障并恢复模型值。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (nonatomic,strong) CALayer *testLayer;
@property (nonatomic,strong) UILabel *testLabel;

@end

和ViewController.m

 #import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGRect frame = CGRectMake(0.0, 0.0, 200.0, 20.0);
    UISlider *slider = [[UISlider alloc] initWithFrame:frame];
    [slider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
    [slider setBackgroundColor:[UIColor clearColor]];
    slider.minimumValue = 0.0;
    slider.maximumValue =  1;
    slider.continuous = YES;
    slider.value = 0.0;
    slider.center = CGPointMake(self.view.center.x, self.view.bounds.size.height - 30);
    [self.view addSubview:slider];

    self.testLabel = [[UILabel alloc]initWithFrame:CGRectMake(40, 30, self.view.bounds.size.width - 80, 40)];
    self.testLabel.text = @"0";
    [self.testLabel setTextColor:[UIColor blackColor]];
    [self.view addSubview:self.testLabel];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.testLayer = [CALayer layer];
    self.testLayer.frame = CGRectMake(100, 100, 30, 30);
    self.testLayer.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:self.testLayer];
}

-(void)sliderAction:(id)sender
{
    self.testLayer.speed = 0;
    UISlider *slider = (UISlider*)sender;
    float value = slider.value * 3.0; //3 is duration so we need to normalize slider
    NSLog(@"The value is %f",value);
    self.testLabel.text = [NSString stringWithFormat:@"%f",value];

    [_testLayer removeAllAnimations];
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.y"];
    //   //get the real position and add it to the keyframe
    CGFloat positionY = self.testLayer.position.y;
    //I did make A the current position to make it smooth
    animation.values = @[@(positionY), @100, @250];
    animation.duration = 3.0;
    animation.fillMode = kCAFillModeForwards;
    //this line fixes readding the animation over and over with a timeOffset
    animation.beginTime = CACurrentMediaTime();
    [animation setRemovedOnCompletion:NO];


    [self.testLayer addAnimation:animation forKey:@"animation"];

    //updating the offset on the layer and not the animation
    self.testLayer.timeOffset = value + CACurrentMediaTime();


}

@end

干杯

答案 2 :(得分:-1)

尝试在addAnimation

之后将图层的位置设置为目标

[layer addAnimation:animation forKey:@&#34; basic&#34;];

layer.position = CGPointMake(x,y);