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

Ios 位于关键帧值中间的CALayer的CAKeyframeAnimation?,ios,objective-c,calayer,caanimation,cakeyframeanimation,Ios,Objective C,Calayer,Caanimation,Cakeyframeanimation,假设我想将a层从a点移动到B点,然后移动到C点: CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; animation.values = @[pointA, pointB, pointC]; animation.duration = 1; 将动画添加到层后,我将其速度设置为0: animation.speed = 0; 因此,我可以使用滑块调整层的位置: lay

假设我想将a
层从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;
但是,如果我的层的当前位置在
点B
,在添加动画后,它会移回
点A
,即使我将
层的
时间偏移设置为0.5

有什么我错过的吗

谢谢

更新: 为了更好地说明问题,下面是一些测试代码:

- (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;
}

testLayer
的Y位置是100,而添加的动画具有键值50、100和150,添加动画时,
testLayer
将移动到第一个键值位置50,但如何防止这种情况?我尝试设置self.testLayer.timeOffset=0.5,但没有帮助。

尝试在添加动画后将层的位置设置为目标

[层添加动画:动画分叉:@“基本”]


layer.position=CGPointMake(x,y)

面纱后面有一些神秘的功能,在我自己做了一些实验后,似乎是
timeOffset
setter在动画与一段时间后发出的调用不同后立即发出的调用:(这是在Swift中,但概念是相同的)

这会以某种方式为层下的所有动画设置一个“全局”偏移,如果以这种方式调用
timeOffset
setter,则所有动画将在0.5s后开始工作

/* 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: "...")
但是,由于点
A
B
C
不一定形成闭合路径,因此在滑块值(即
timeOffset
)超过
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)

这使得动画能够按照您的需要正确地执行,但肯定有更好的方法,我也很好奇为什么
时间偏移设置器的行为会有所不同,希望有擅长
动画的人能够解释。

老实说,这是最难回答的问题之一。诚然,CALayers和CAAnimation的时间安排通常相当复杂。通常,在添加动画时更新模型值是很好的,但是您希望通过百分比/滑块来完成,所以我完全理解这是一个挑战。Cytus的答案是非常遥远的,在添加新动画时设置layer.timeOffset时似乎有一个bug。看见在动画本身上设置时间偏移有效,但当滑块处于最大值时,值被删除。如果在ViewDidDisplay中添加动画,并且仅在滑块动作方法中更新时间偏移,则该方法也可以完美工作。我开始记录图层的时间值,注意到beginTime有些疯狂的东西。简单的回答是,当您添加新动画时,您必须将开始时间(特别是在本例中)设置为CACurrentMediaTime(),并且所有这些都可以完美地工作。奇怪的是,通常我用它来延迟,但是关于反复替换当前动画的一些东西会产生对这个属性的需要,我一直认为这是可选的。我想说的是,在您完成后,您应该删除动画,以防止演示层受到污染,但这取决于您。下面是一个工作示例,它可以将动画保持在适当的位置,而不会出现毛刺并恢复为模型值

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

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

@end

干杯

试试我的答案。只是修正了一些拼写错误,所以应该可以继续了
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

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

@end
 #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