Everyone loves animations and I think every app should make use of them; with care, but that in a future post. The easiest way to add animations is with
animate method, or with
UIViewPropertyAnimators. Pretty straightforward and easy to use, but they don’t support animating
CALayer properties, like
borderWidth. For these, we have
CABasicAnimation, or rather all of its concrete subclasses:
CATransition. In this post we’ll quickly cover
Say we want to animate the
borderColor of a view, this is how we’d go about it:
let borderColorAnimation = CABasicAnimation(keyPath: "borderColor") // 1 borderColorAnimation.duration = 0.15 // 2 borderColorAnimation.fromValue = UIColor.red.cgColor // 3 borderColorAnimation.toValue = UIColor.blue.cgColor // 4 borderColorAnimation.timingFunction = CAMediaTimingFunction(name: .linear) // 5 borderColorAnimation.beginTime = CACurrentMediaTime() + 0.2 // 6 view.layer.add(borderColorAnimation, forKey: "blueBorderColorAnimation") // 7
First, we initialize one with a
keyPath, the property of the layer we want to animate (1). We then set some properties, like the duration (2), the starting (3) and end (4) colors, the timing function (5) and a start time (6). Lastly, we have to add this animation to our layer under a
key, which is just a string that identifies the animation.
toValue are of type
Any, since they have to be able to accept pretty much anything, from
CGColors (our case), to
CGRects if we want to animate the
Floats, if we want to animate the
borderWidth (like below).
The timing functions are the equivalent of the ones used on
UIViewAnimationOptionCurveLinear in our case.
CACurrentMediaTime() gives us the current time, to which we add
0.2 seconds, since in our case, we want it to start with a slight delay.
Now, if we want to animate the
borderWidth at the same time, we’d go about in a similar fashion:
let borderWidthAnimation = CABasicAnimation(keyPath: "borderWidth") borderWidthAnimation.duration = 0.15 borderWidthAnimation.fromValue = 0.5 borderWidthAnimation.toValue = 1.5 borderWidthAnimation.timingFunction = CAMediaTimingFunction(name: .linear) borderWidthAnimation.beginTime = CACurrentMediaTime() + 0.2 view.layer.add(borderWidthAnimation, forKey: "tripleBorderWidthAnimation")
The same properties are set, except the
toValue, which are now
This already seems a bit wrong; we’re duplicating a lot of code, like the
timingFunction and the
beginTime. Luckily for us, we can make use of
CAAnimationGroups. These allow multiple animations to be grouped and run together.
let borderColorAnimation = CABasicAnimation(keyPath: "borderColor") // 1 borderColorAnimation.fromValue = UIColor.red.cgColor borderColorAnimation.toValue = UIColor.blue.cgColor let borderWidthAnimation = CABasicAnimation(keyPath: "borderWidth") // 2 borderWidthAnimation.fromValue = 0.5 borderWidthAnimation.toValue = 1.5 let group = CAAnimationGroup() // 3 group.duration = 0.15 group.timingFunction = CAMediaTimingFunction(name: .linear) group.beginTime = CACurrentMediaTime() + 0.2 group.animations = [borderColorAnimation, borderWidthAnimation] // 4 view.layer.add(group, forKey: "borderChangeAnimationGroup") // 5
We first create our
borderColor (1) and
borderWidth (2) animations as before, but we only set the
toValue. We then create a group (3), set the remaining properties on it —
beginTime — and also, a new one, called
animations; here we set an array of
CAAnimations that we want to run concurrently in this group. Lastly, we add the group to the layer, just like we previously did with the individual
CABasicAnimations — the group is just a subclass of
This will run both animations at the same time, with the same time properties. It also allows us to write cleaner code that’s easier to reason with, but also less prone to errors, since everything is configured in one place. Imagine having 5 of these animations, then realizing that something is wrong — we’d have to change the values in all 5 places!