Today is the day when we start looking at animations.
The setup is pretty straightforward so, in brief:
- create a new iOS Single View App project and call it “Animation”
- In Project > Deployment Info, select iPad as the only device and uncheck “Portrait” and “Upside Down” rom the Device Orientation category
- Import the “firstname.lastname@example.org” image from the Hacking with Swift GitHub repository into the Assets.xcassets folder.
Preparing for action
First of all we need to place a
UIButton with the title “Tap” at the bottom of the screen and set its constraints to the parent view to be “Bottom Space to Safe Area” and “Center Horizontally in Safe Area”. This will make the button stay to the center-bottom of the screen.
Via the Assistant Editor we are going to create an action called
tapped() for our button but before filling it with code we need to create an implicitly unwrapped optional
UIImageView property called
imageView and an integer property called
currentAnimation set to
viewDidLoad() we are going to initialise our image by calling the
UIImageView initialiser called
image and then passing it the
UIImage(named:)initialiser with the name “penguin”, which should hopefully correspond to what we have in the assets catalog. We are going to place the center of the image view to the
x: 512, y: 384 coordinates (which should correspond to the center of the iPad, and then add it as a subview to our view.
Last but not least we are filling in our
tapped method. Each time our button is being tapped we are going to increase the
currentAnimation property by
1 and, if that value rises above
7, we are going to reset it to
To handle our animation we need to update our
tapped method. First we need to hide the button when it is tapped for the first time. Second we need to call the
UIView.animate(withDuration:delay:options:animations:completion:) method to manage the animation itself. The
withDuration parameter will be set to
1 (second) and the
0 (always seconds). The
options parameter is accepting an array of
AnimationOptions and it defaults to an empty array but this time we want to specify the empty array as we may want to experiment with it at a later stage. The last two parameters accept a closure each and we are going to
switch over the
currentAnimation property to decide which animation will be executed. By now we will just provide a case for
0 and a case for
default, both of which will
break out of the
completion handler will accept a boolean parameter that we will call
finished but I guess we could also just call it
_ and simply show the button again. Third, but it is better if you do it first!, make sure the
sender parameter is set to
UIButton instead of
Any. I wonder why we didn’t set it so at the beginning as we had a chance to do so during the action’s creation.
To animate our penguin we need to meet
CGAffineTransform which is a
struct defined as an “affine transformation matrix for use in drawing 2D graphics”. This sounds cryptic enough but here is the best part of the Documentation description:
An affine transformation matrix is used to rotate, scale, translate, or skew [distort] the objects you draw in a graphics context. The
CGAffineTransformtype provides functions for creating, concatenating, and applying affine transformations.
Even if I like mathematics a lot I am not expert enough to delve into matrix calculus (yet!).
By calling the
transform property on our
imageView we can specify the transform applied to the view, relative to the center of its bounds. We can use this property to scale or rotate the view’s frame rectangle within its superview’s coordinate system. In case we would want to change the position of the view itself, the should modify its
cente property instead. The default value of this property is
CGAffineTRansformIdentity (which we will meet in just a second).
Transformations occur relative to the view’s anchor point and, by default, the anchor point is equal to the center point of the frame rectangle.
So, here are the 8 cases we will use for this animation session: for
case 0 we will set the
self.imageView.transform property to
CGAffineTransform(scaleX: 2, y: 2), that is we will increase both its dimensions to the double. The default UIKit animations have an “ease in, ease out” curve, that is, they start slowly, they accelerate and they slow down before finishing their path. This produces the pleasing effect to the eye we all know.
case 1 we are going to set the
transform property to
.identity which, as we said just before, represents the original position of the view.
case 2 we are going to use the
translationX:y: initialiser for our transform to move up and left our penguin by a said amount (in this case
case 3 we are going to repeat
case 1. I don’t remember but I guess we could write
case 1, 3, 5: and it should work as far as I know.
rotationAngle initialiser for our
case 4 (which accepts an angle in radians as its only parameter) we can rotate the view. We need to be extra careful with this because we need to specify the value as a
CGFloatand because Core Animation will always take the shortest route to make the rotation work (so don’t try to make more than
.pi rotation as it will just “seem” not to work, for example if you use
pi * 2 which will result in the view not moving at all!).
For the final
case 6 we will change the
0.1(making the image almost transparent) and then its
UIColor.green (shouldn’t we add
case 7 we will just set the
alpha back to
1 and the
As a final special effect we are going to add two parameters after
initialSpringVelocity. The first parameter describes the damping ratio for the spring animation as it approaches its quiescent state. To smoothly decelerate the animation without oscillation, we should use a value of 1, while employing a ratio closer to zero (for example
0.5 as in our case) will increase its oscillation. The second sets the initial spring velocity (I never understood if velocity and speed are the same thing or not in English). If we want a smoother start to the animation, we should match this value to the view’s velocity as it was prior to attachment. Translated from old-Assyrian language, a value of
1 corresponds to the total animation distance traversed in one second. I know, my dead languages translation skills are not so valuable as I didn’t even understand what I said myself, but … In the project we set a value of
5 but I have no idea where we took this from…
Anyway, it seems nothing but this project is finished.
You can find the repository here.
Please don’t forget to drop a hello and a thank you to Paul for all his great work (you can find him on Twitter) and don’t forget to visit the 100 Days Of Swift initiative page.
Thanks for reading!
Till the next one!
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!