Ios 位于关键帧值中间的CALayer的CAKeyframeAnimation?
假设我想将aIos 位于关键帧值中间的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点移动到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