So I Wanted To Recreate Something From Dribbble
A week or-so ago, I was clicking around on Dribbble, which just happens to be one of my all-time favourite websites. I sometimes browse it while waiting for my code to compile. One day, I found a rather delightful loading-indicator GIF featured on the front page.
It looked something like this:
In the days since, I have been unable to find the original animated GIF with which I fell so deeply in love.
It had such a profound impact on my curiosity, that I could picture it in my head as clearly as if I were staring at the image in my browser. I decided, after being momentarily frustrated over not being able to find it again, that I would “brain-dump” what I had either imagined or actually seen —I’m now uncertain whether I actually saw it — into a tutorial.
Bring It Out, Bring It Out...
Go ahead and create a new, empty Xcode project. You should use the Single-view application template, and ensure that the language is set to Swift. Don’t pay attention to the checkboxes asking you to use Core Data or any of that hooey, because you won’t be needing them.
Now, create a new Swift file. Name it whatever you wish. This file should be a subclass of CALayer.
The first thing I’m going to add is a custom initialiser. This is because we’re wanting to supply the number of items for the stack, and it’s nicer to put this kind of thing in an initialiser. Furthermore, I’ll add a variable to hold the base colour, and one to hold the size of each stack item.
Whenever you insert sublayers into a parent — think of them as children and parents if that makes it easier on you —layer, they will get clipped during animation because the parent is only a certain size. That isn’t a pretty effect, and it ruins many animations. So I’ll disable that, too.
For the sake of keeping things brief — really, I could go on all day — I’ll add a loop into the init method we just wrote. Instead of having six lines describing my shape, I’ll put that into a function and make the loop more readable.
Here’s a sample I prepared earlier:
If you noticed, the colour variable we added is calling a method that we don’t have yet. Since we’re going to be messing with UIColor a little to set the brightness — or hue or saturation, whatever tickles your fancy really — of the shapes, save this for use later:
Trying to find different shades of the same colour using a colour wheel — lets be honest here — sucks unless you’re a graphic designer with a good eye. With this little extension, you can have a “base” colour, and create darker or lighter variants of it as required throughout your app.
With me so far?
Okay, so CALayer and CAShapeLayer are two quite powerful and confusing components of CoreAnimation. So I’ll give a quick rundown of what we’ve achieved so far.
- Created a subclass of CALayer
- Written a convenience initialiser, that takes the number of shapes to draw.
- Added a variable to hold the base colour, the item size and the size of the parent layer.
- Written a loop to create, position and translate each shape layer in the parent layer.
- Applied a fill colour to the layers.
- Reversed the sublayers array.
- Centred the parent layer in the superview.
- Adjusted the anchor point of each shape layer.
- Converted degrees into radians.
That point regarding the reversal of sublayers is important, because of the way we’re presenting the stack. Any runtime modification to it, such as adjusting the distance between each object on the Z axis, will break the effect we’re creating without the reversal first taking place.
CAShapeLayer is one type of CALayer to have a fill colour and path. We are modifying these properties to draw what we like on the screen.
Whenever we modify the anchor point of a layer, the layer’s frame will shift. This isn’t at all desirable for the effect we’re trying to achieve, and if you’ve ever tried modifying this property, chances are it wasn’t what you wanted either.
By default, a layer has an anchor point of 0,0, which means that all animation performed on it, will happen around that point. To achieve our goal, we must anchor the layer in its centre.
I'm A House On Fire!
Go ahead and paste this into your class, underneath the last squiggly bracket in your file.
You’ve just extended the SpinnyMcSpinface — sorry for that, by the way — class with extra functionality. One function starts the animation, one ends it, and the other one generates the CABasicAnimation that we apply to each child shape in the loop. 🎉
Before we run the app to see the animation working — it won’t, at the minute — make sure your main view controller file has the following lines in place:
If we run the app now you’ll see that the animation is taking place. 🎉
Each layer has a slightly different colour, creating a nice effect. But what’s wrong with this picture?
You can see that the layers are all positioned correctly, but the angle is wrong. So let’s correct that now. Add the following function:
And call it right after centerInSuperlayer() in the initialiser, as follows:
What that will do, is rotate the parent layer 60 degrees along its X-axis. Once we run this, we should see the stack…or will we?
I Wanna Keep Burnin!
If you run the animation again after doing the above, it looks like this:
This is because CALayer itself doesn’t actually render depth. We’ve positioned everything to be beneath the other using zPosition, but as soon as you rotate it expecting to see a “stack,” you’ll be sorely disappointed.
You can however position items into a stack with ordinary CALayer, although a rotation similar to the above wouldn’t be possible. This is because any new transform applied on top of an existing one will remove the old.
That’s where CATransformLayer comes in. It’s a nifty little layer type that supports depth out of the box.
To finish, and achieve the effect desired, simply change your subclass from CALayer to CATransformLayer, and re-run the app. 🎉
If you liked this post, consider giving me a clap, or seven. このストーリーを好きなら、「拍手」して押して頂けないでしょうか。