CABasicAnimation with keypath "bounds" not working

KSC picture KSC · Dec 20, 2014 · Viewed 11.5k times · Source

I have the following code to animation bounds property of CALayer using CABasicAnimation. But the code doesn't seem to work.

    let fromValue = textLabel.layer.bounds
    let toValue = CGRectMake(textLabel.layer.bounds.origin.x, textLabel.layer.bounds.origin.y,   textLabel.layer.bounds.width, textLabel.layer.bounds.height + 50)

    let positionAnimation = CABasicAnimation(keyPath: "bounds")
    positionAnimation.fromValue = NSValue(CGRect: fromValue)
    positionAnimation.toValue = NSValue(CGRect: toValue)
    positionAnimation.duration = 1
    positionAnimation.fillMode = kCAFillModeBoth
    positionAnimation.removedOnCompletion = false

    textLabel.layer.addAnimation(positionAnimation, forKey: "bounds")

Answer

matt picture matt · Dec 20, 2014

Your code does actually work. If you run your code and then switch on View Debugging in Xcode, you will see that the height of the label has increased. The "problem" is that a UILabel in iOS 8 draws itself (its text and its background, if it has one) the same way even after its layer height has been artificially increased in this way. (I believe that this is because the label draws itself with a special clipping region that is based on its text contents.)

To prove this to yourself, try it on a plain vanilla UIView (with a colored background) instead of a label. I've taken the liberty of cleaning up your code (you should never misuse fillMode and removedOnCompletion the way you are doing - it just shows a lack of understand of what animation is):

    let fromValue = view2.layer.bounds.height
    let toValue = view2.layer.bounds.height + 50
    CATransaction.setDisableActions(true)
    view2.layer.bounds.size.height = toValue
    let positionAnimation = CABasicAnimation(keyPath: "bounds.size.height")
    positionAnimation.fromValue = fromValue
    positionAnimation.toValue = toValue
    positionAnimation.duration = 1
    view2.layer.addAnimation(positionAnimation, forKey: "bounds")

You will see that that works perfectly. Now change view2 back to textLabel throughout. It still works, it's just that there is nothing to see.

Another way to prove this to yourself is to drop the whole animation and just change the label's layer height:

    self.textLabel.layer.bounds.size.height += 50

You will not see anything happen. So there is no bug in your animation; it's all about the way labels are drawn.

You can make the change visible by changing the view instead of the layer:

    self.textLabel.bounds.size.height += 50

One additional "problem", though, is that the animation is not animating. Again this is because labels are drawn in a special way.

So whatever it is you're trying to accomplish it, you'll have to do it in a different way. You might have a clear label in front of a colored view and animate the change in height of the view; we've already proven that that works.