如何为 CALayer 从可变起点到给定终点设置动画
How to animate a CALayer from a variable start point to a given end point
CALayer动画
我的应用程序的目的是在 TableView 中显示 ToDos/Tasks。所有任务都有开始值和结束值。
对于第一个任务,我想显示一个进度指示器,以便更好地了解剩余时间。对于背景,我在 ViewController 中有一个带有 autoLayout 约束的 UIView。我用于动画的 CALayer 是 backgroundView 的子层。 x 轴上层的起点是可变的,因此即使重新加载应用程序,计时器也设置正确。终点是一个绝对值(背景视图的全宽)。这些参数都运行良好,只是动画不起作用。我正在设置动画应该开始的值,以及剩余的持续时间,但 CALayer 不会移动。我曾尝试使用 UIView.animate 和 CABasicAnimation 来操纵图层,但在每种情况下,将图层设置到其起始位置后,动画都不会启动。
(https://ise-technology.com/cdn/stack-overflow/calayertimer/timer)
// func for setting setting the CALayer and the BackgroundView for the given values
func setTimerView(isActive: Bool, color: UIColor, animationDuration: Double){
if isActive == true {
configureBackgroundView(bool: true) // just sets the height for the background view
backgroundTimerView.backgroundColor = color.withAlphaComponent(0.3)
timerView.backgroundColor = color.cgColor
setAnimationForDuration(duration: animationDuration)
} else {
configureBackgroundView(bool: false)
timerView.backgroundColor = UIColor.clear.cgColor
}
}
// func where the animation should be embedded
func setAnimationForDuration(duration: Double) {
let startPoint = setStartPoint(duration: duration)
timerView.frame.origin.x = CGFloat(-startPoint)
// after updating the origin X of the Layer to the current position, its not possible to start an animation. My attempts to here place in this func
let animation = CABasicAnimation()
animation.duration = duration
animation.toValue = timerView.frame.origin.x = 0
timerView.add(animation, forKey: nil)
}
您应该能够使用 CABasicAnimation 来完成您想要完成的任务。我已经在 Objective-C 中发布了我的答案,但我确信将其改编为 Swift 也相对容易。
我认为您还遗漏了一些额外的东西:
设置图层的锚点和位置:
self.progressLayer.position = CGPointZero;
self.progressLayer.anchorPoint = CGPointZero;
为 bounds.size.width 键路径设置 CABasicAnimation
CABasicAnimation *progressAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
将填充模式设置为转发,完成后删除为否
progressAnimation.fillMode = kCAFillModeForwards;
[progressAnimation setRemovedOnCompletion:NO];
下面是一个仅使用一个视图控制器的示例,它使进度条从中间点到终点具有动画效果:
@interface ViewController ()
@property (strong, nullable) IBOutlet UIView *backgroundView;
@property (strong, nullable) CALayer *progressLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.progressLayer = [[CALayer alloc] init];
[self.progressLayer setFrame:CGRectMake(self.backgroundView.bounds.origin.x, self.backgroundView.bounds.origin.y, self.backgroundView.bounds.size.width / 2.0f, self.backgroundView.bounds.size.height)];
self.progressLayer.position = CGPointZero;
self.progressLayer.anchorPoint = CGPointZero;
self.progressLayer.backgroundColor = [UIColor greenColor].CGColor;
[self.backgroundView.layer addSublayer:self.progressLayer];
}
- (IBAction)animateProgress:(id)sender {
[self.progressLayer removeAnimationForKey:@"progressAnimation"];
CABasicAnimation *progressAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
progressAnimation.duration = 2.0f;
progressAnimation.toValue = @(self.backgroundView.bounds.size.width);
progressAnimation.fillMode = kCAFillModeForwards;
[progressAnimation setRemovedOnCompletion:NO];
[self.progressLayer addAnimation:progressAnimation forKey:@"progressAnimation"];
}
@end
您应该能够在自定义 tableview 单元格的 awakeFromNib + 一些触发动画的方法中执行类似的操作。
上面的例子是这样的:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var backgroundView: UIView!
var progressLayer : CALayer!
override func viewDidLoad() {
super.viewDidLoad()
self.setUpLayer()
}
func setUpLayer(){
let x = self.backgroundView.bounds.origin.x
let y = self.backgroundView.bounds.origin.y
let height = self.backgroundView.bounds.size.height
let width = self.backgroundView.bounds.size.width
self.progressLayer = CALayer.init()
self.progressLayer.frame = CGRect(x: x, y: y, width: 0, height: height)
self.progressLayer.position = CGPoint.zero
self.progressLayer.anchorPoint = CGPoint.zero;
self.progressLayer.backgroundColor = UIColor.red.cgColor;
self.backgroundView.layer.addSublayer(self.progressLayer)
}
@IBAction func buttonAction(_ sender: Any) {
// self.progressLayer.removeAnimation(forKey: "progressAnimation")
var progressAnimation = CABasicAnimation(keyPath: "bounds.size.width")
progressAnimation.duration = 2.0
progressAnimation.toValue = self.backgroundView.bounds.width
progressAnimation.fillMode = .forwards
progressAnimation.isRemovedOnCompletion = false
self.progressLayer.add(progressAnimation, forKey: "progressAnimation")
}
}
CALayer动画
我的应用程序的目的是在 TableView 中显示 ToDos/Tasks。所有任务都有开始值和结束值。
对于第一个任务,我想显示一个进度指示器,以便更好地了解剩余时间。对于背景,我在 ViewController 中有一个带有 autoLayout 约束的 UIView。我用于动画的 CALayer 是 backgroundView 的子层。 x 轴上层的起点是可变的,因此即使重新加载应用程序,计时器也设置正确。终点是一个绝对值(背景视图的全宽)。这些参数都运行良好,只是动画不起作用。我正在设置动画应该开始的值,以及剩余的持续时间,但 CALayer 不会移动。我曾尝试使用 UIView.animate 和 CABasicAnimation 来操纵图层,但在每种情况下,将图层设置到其起始位置后,动画都不会启动。
(https://ise-technology.com/cdn/stack-overflow/calayertimer/timer)
// func for setting setting the CALayer and the BackgroundView for the given values
func setTimerView(isActive: Bool, color: UIColor, animationDuration: Double){
if isActive == true {
configureBackgroundView(bool: true) // just sets the height for the background view
backgroundTimerView.backgroundColor = color.withAlphaComponent(0.3)
timerView.backgroundColor = color.cgColor
setAnimationForDuration(duration: animationDuration)
} else {
configureBackgroundView(bool: false)
timerView.backgroundColor = UIColor.clear.cgColor
}
}
// func where the animation should be embedded
func setAnimationForDuration(duration: Double) {
let startPoint = setStartPoint(duration: duration)
timerView.frame.origin.x = CGFloat(-startPoint)
// after updating the origin X of the Layer to the current position, its not possible to start an animation. My attempts to here place in this func
let animation = CABasicAnimation()
animation.duration = duration
animation.toValue = timerView.frame.origin.x = 0
timerView.add(animation, forKey: nil)
}
您应该能够使用 CABasicAnimation 来完成您想要完成的任务。我已经在 Objective-C 中发布了我的答案,但我确信将其改编为 Swift 也相对容易。
我认为您还遗漏了一些额外的东西:
设置图层的锚点和位置:
self.progressLayer.position = CGPointZero;
self.progressLayer.anchorPoint = CGPointZero;
为 bounds.size.width 键路径设置 CABasicAnimation
CABasicAnimation *progressAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
将填充模式设置为转发,完成后删除为否
progressAnimation.fillMode = kCAFillModeForwards;
[progressAnimation setRemovedOnCompletion:NO];
下面是一个仅使用一个视图控制器的示例,它使进度条从中间点到终点具有动画效果:
@interface ViewController ()
@property (strong, nullable) IBOutlet UIView *backgroundView;
@property (strong, nullable) CALayer *progressLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.progressLayer = [[CALayer alloc] init];
[self.progressLayer setFrame:CGRectMake(self.backgroundView.bounds.origin.x, self.backgroundView.bounds.origin.y, self.backgroundView.bounds.size.width / 2.0f, self.backgroundView.bounds.size.height)];
self.progressLayer.position = CGPointZero;
self.progressLayer.anchorPoint = CGPointZero;
self.progressLayer.backgroundColor = [UIColor greenColor].CGColor;
[self.backgroundView.layer addSublayer:self.progressLayer];
}
- (IBAction)animateProgress:(id)sender {
[self.progressLayer removeAnimationForKey:@"progressAnimation"];
CABasicAnimation *progressAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
progressAnimation.duration = 2.0f;
progressAnimation.toValue = @(self.backgroundView.bounds.size.width);
progressAnimation.fillMode = kCAFillModeForwards;
[progressAnimation setRemovedOnCompletion:NO];
[self.progressLayer addAnimation:progressAnimation forKey:@"progressAnimation"];
}
@end
您应该能够在自定义 tableview 单元格的 awakeFromNib + 一些触发动画的方法中执行类似的操作。
上面的例子是这样的:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var backgroundView: UIView!
var progressLayer : CALayer!
override func viewDidLoad() {
super.viewDidLoad()
self.setUpLayer()
}
func setUpLayer(){
let x = self.backgroundView.bounds.origin.x
let y = self.backgroundView.bounds.origin.y
let height = self.backgroundView.bounds.size.height
let width = self.backgroundView.bounds.size.width
self.progressLayer = CALayer.init()
self.progressLayer.frame = CGRect(x: x, y: y, width: 0, height: height)
self.progressLayer.position = CGPoint.zero
self.progressLayer.anchorPoint = CGPoint.zero;
self.progressLayer.backgroundColor = UIColor.red.cgColor;
self.backgroundView.layer.addSublayer(self.progressLayer)
}
@IBAction func buttonAction(_ sender: Any) {
// self.progressLayer.removeAnimation(forKey: "progressAnimation")
var progressAnimation = CABasicAnimation(keyPath: "bounds.size.width")
progressAnimation.duration = 2.0
progressAnimation.toValue = self.backgroundView.bounds.width
progressAnimation.fillMode = .forwards
progressAnimation.isRemovedOnCompletion = false
self.progressLayer.add(progressAnimation, forKey: "progressAnimation")
}
}