Hacking with Swift – Learning Project 15

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 “penguin@2x.png” 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 0.

Inside 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 0.

Switch, case, animate:, animate(withDuration:)

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 delay to 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 switch. 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.

Transform: CGAffineTransform

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 CGAffineTransform type 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.

For 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.

For 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 -256, -256). For 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.

Using the 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 alpha to 0.1(making the image almost transparent) and then its background to UIColor.green (shouldn’t we add .cgColor here?!).

For case 7 we will just set the alpha back to 1 and the background to .clear.

As a final special effect we are going to add two parameters after delay: usingSpringWithDamping and 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!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: