pizzafilms
pizzafilms

Reputation: 4019

CABasicAnimation ignores duration

I can't seem to control the duration of a simple Core Animation whether I use a CATransation or not.

The transaction completion code fires after my duration setting like I'd expect, but the rotation animation always takes about 0.5 seconds no matter what duration it's set to.

But if I comment out the last line that sets the transform, the animation moves at the proper speed, but then snaps back to the original rotation.

What am I missing?

class MyControl: NSControl {
    var shapeLayer = CAShapeLayer()

    required override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        self.wantsLayer = true
        self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay

        // create a triangle shape
        let path = NSBezierPath()
        path.moveToPoint(CGPointMake(45.0, 25.0))
        path.lineToPoint(CGPointMake(5.0, 5.0))
        path.lineToPoint(CGPointMake(5.0, 45.0))
        path.lineToPoint(CGPointMake(45.0, 25.0))
        path.closePath()
        shapeLayer.frame = self.bounds
        shapeLayer.path = path.toCGPath()
        shapeLayer.fillColor = NSColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0).CGColor
        self.layer!.addSublayer(shapeLayer)
    }

    override func mouseUp(theEvent: NSEvent){
        let duration = 2.0
        let endAngle = -90.0
        let startAngle = shapeLayer.valueForKey("transform.rotation")

        CATransaction.begin()
        CATransaction.setAnimationDuration(duration)
        CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut))
        CATransaction.setCompletionBlock({
            Swift.print("this fires after 2 seconds")
        })

        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotateAnimation.removedOnCompletion = true
        rotateAnimation.autoreverses = false
        rotateAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        rotateAnimation.duration = duration
        rotateAnimation.fromValue = startAngle
        rotateAnimation.toValue = endAngle.degreesToRadians
        shapeLayer.addAnimation(rotateAnimation, forKey: rotateAnimation.keyPath)
        CATransaction.commit()

        // mystery line...
        shapeLayer.transform = CATransform3DMakeRotation(endAngle.degreesToRadians, 0.0, 0.0, 1.0)
    }


}

On a side note, I made use of a couple of useful extensions... toCGPath in NSBezierPath and degreesToRadians for CGFloat.

Upvotes: 1

Views: 3111

Answers (2)

khoiNN
khoiNN

Reputation: 45

I think the problem is because your startAngle and endAngle. Try to change it like this. This works for me

private let startAngle: CGFloat = CGFloat(-M_PI_2) 
private let endAngle: CGFloat = CGFloat(-M_PI_2) + CGFloat(M_PI * 2)

Upvotes: 0

Shripada
Shripada

Reputation: 6522

The reason why you are seeing this strange behaviour is usage of explicit CATransaction begin--commit. This will in turn add a nested CATransaction within the one already owned by the run loop. So what happens is, your inner transaction gets committed, but before the associated animations finish, you have an immediate new value for the transform. This will cause this glitch where your layer will just quickly rotate and vanish.

Instead of this, the straight forward way to do is 1. Create your animation 2. Set a delegate for your animation 3. In delegate call back set the target value you would want the layer to remain in.

 override func mouseUp(theEvent: NSEvent){
    let duration = 2.0
    let endAngle = -90.0
    let startAngle = shapeLayer.valueForKey("transform.rotation")

    //Set up animation
    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnimation.removedOnCompletion = true
    rotateAnimation.autoreverses = false
    rotateAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    rotateAnimation.duration = duration
    rotateAnimation.fromValue = startAngle
    rotateAnimation.toValue = endAngle * 3.142/180

    //Setup delegate
    rotateAnimation.delegate = self

    shapeLayer.addAnimation(rotateAnimation, forKey: @"rotationAnimation")

  }


  public override func animationDidStop(anim: CAAnimation, finished flag: Bool){
    shapeLayer.transform  = CATransform3DMakeRotation(CGFloat(-90.0 * 3.142/180), 0.0, 0.0, 1.0)
  }

P.S: I have not run this code, and also note sure what value you want to set for the transform of your layer once the animation completes.

Upvotes: 1

Related Questions