Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CABasicAnimation reverse(backwards)

I'm a bit struggling with this simple line animation. I figured out how to pause it, but what I need is to be able to reverse animation back to starting point from the moment I call function resetAnimation().

let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
    let pathLayer = CAShapeLayer()

     func lineAnimation() {

        let path = UIBezierPath()
        let screenWidth = self.view.bounds.width
        let screenHeight = self.view.bounds.height
        path.moveToPoint(CGPointMake(screenWidth, screenHeight / 2))
        path.addLineToPoint(CGPointMake(screenWidth - screenWidth, screenHeight / 2))

        self.pathLayer.frame = self.view.bounds
        self.pathLayer.path = path.CGPath
        self.pathLayer.strokeColor = UIColor.whiteColor().CGColor
        self.pathLayer.fillColor = nil
        self.pathLayer.lineWidth = 3.0
        self.pathLayer.lineCap = kCALineCapRound
        self.pathLayer.speed = 1

        self.view.layer.addSublayer(pathLayer)

        self.pathAnimation.duration = 5.0
        self.pathAnimation.fromValue = 0.0
        self.pathAnimation.toValue = 1.0

        pathLayer.addAnimation(pathAnimation, forKey: "animate")

    }

    func pauseAnimation() {

        let pausedTime = pathLayer.convertTime(CACurrentMediaTime(), fromLayer: nil)
        pathLayer.speed = 0
        pathLayer.timeOffset = pausedTime

    }

    func resetAnimation() {


    }
like image 510
Linas Vildziunas Avatar asked Feb 02 '16 20:02

Linas Vildziunas


2 Answers

You just need to create a new animation and remove the old one. Your starting point for the new animation will be the current value of that property in your presentation layer. I'd also recommend setting the frame of your shape layer to the bounds of the actual bezier shape instead of the entire view - its a good habit to be in when you start moving things around and scaling/rotating/etc. Otherwise you're gonna be faced with a bunch of funky conversions or anchor point changes.

Here's what I'd do:

let pathLayer = CAShapeLayer()

// first, separate your drawing code from your animation code.
// this way you can call animations without instantiating new objects
func drawLine() {
    let path = UIBezierPath()
    // draw your path with no position translation.. move the layer
    path.moveToPoint(CGPointMake(view.bounds.width, 0))
    path.addLineToPoint(CGPointMake(0, 0))
    pathLayer.frame = path.bounds
    // this line sets the position of the layer appropriately
    pathLayer.position = view.bounds.width - pathLayer.bounds.width / 2
    pathLayer.path = path.CGPath
    pathLayer.strokeColor = UIColor.whiteColor().CGColor
    pathLayer.fillColor = nil
    pathLayer.lineWidth = 3.0
    pathLayer.lineCap = kCALineCapRound
    view.layer.addSublayer(pathLayer)
}

func lineAnimation() {
    let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
    pathAnimation.duration = 5.0
    pathAnimation.fromValue = 0.0
    pathAnimation.toValue = 1.0
    pathLayer.addAnimation(pathAnimation, forKey: "strokeEnd")

}

func reverseAnimation() {
    let revAnimation = CABasicAnimation(keyPath: "strokeEnd")
    revAnimation.duration = 5.0
    // every Core Animation has a 'presentation layer' that contains the animated changes
    revAnimation.fromValue = pathLayer.presentationLayer()?.strokeEnd
    revAnimation.toValue = 0.0
    pathLayer.removeAllAnimations()
    pathLayer.addAnimation(revAnimation, forKey: "strokeEnd")
}

Keep in mind you'll also want to set your properties so you retain the end values of the animation.

like image 80
Chris Slowik Avatar answered Nov 20 '22 06:11

Chris Slowik


You could also use the autoreverses property of CAMediaTiming protocol, which is complied by CABasicAnimation

like image 33
toupper Avatar answered Nov 20 '22 05:11

toupper