Tim
Tim

Reputation: 13

How to animate a CALayer from a variable start point to a given end point

CALayer animation

My App's purpose is to display ToDos/Tasks in a TableView. All the tasks have a start and an end value.

For the first task I want to display a progress indicator for a good overview, about the time that's left. For the Background I have an UIView with autoLayout constraints in my ViewController. The CALayer, that I am using for the animation is a sublayer from the backgroundView. The start point of the layer on the x-axis is variabel, so even with a reload of the app, the timer is set correctly. The end point is an absolut value (the full width of the background view). Those parameters are all working perfectly, just the animation does not work. I'm setting the values from where the animation should start, and the duration which is left, but the CALayer is not going to move. I have tried to manipulate the Layer with UIView.animate and with CABasicAnimation but in every case, after setting the layer to its start position, the animation does not start.

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

Upvotes: 1

Views: 2076

Answers (2)

Talha Rasool
Talha Rasool

Reputation: 1152

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")

        
    }
    

}

Upvotes: 1

R4N
R4N

Reputation: 2595

You should be able to use CABasicAnimation for what you're attempting to accomplish. I've posted my answer in Objective-C, but I'm sure it is relatively easy to adapt it for Swift as well.

A couple of additional things that I believe you're missing:

Setting the Anchor point and position of the layer:

    self.progressLayer.position = CGPointZero;
    self.progressLayer.anchorPoint = CGPointZero;

Setting the CABasicAnimation for the bounds.size.width key path

CABasicAnimation *progressAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];

Setting the fill mode to forwards, and remove on completion to NO

    progressAnimation.fillMode = kCAFillModeForwards;
    [progressAnimation setRemovedOnCompletion:NO];

Here's an example using just one view controller an animating a progress bar from the halfway point to the full point:

@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

You should be able to do something similar in a custom tableview cell's awakeFromNib + some method to trigger the animation.

Here's what the example above looks like:

Animate_Progress_Layer

Upvotes: 1

Related Questions