[Download the full Xcode project from GitHub.]
Today, I’m going to push you to understand how how I created the following iPhone animation using Swift 3.0:
I’ll give you everything you need to figure out what I did — but I won’t explain it for you. Explanation will come later (in the next post, I tie all posts in this series, 1, 2, 3, and 4 together). I want you to learn about my design and code, not just copy and paste it. There is a method to my seeming madness. Hang in there with me through this series of posts.
After providing you with diagrams, source code, references, definitions, inline commentary, etc., I want you to be able to answer some questions about building iOS animations, even if it requires you to do some research, before I put all the pieces together in the final chapter (post) in this series. Remember that I started this blog with the intention of helping new/aspiring iOS app developers get started in an exciting, creative, and potentially financially rewarding profession. I don’t want to just provide code for you to copy and paste into your own app projects, I want you to become the best of the best iOS designers and developers. So let me:
- Provide you with some context to remind you where we’re at in this series,
- Remind you of several key concepts required for animations (and almost all general UIView programming),
- Show you some diagrams defining geometric concepts you must learn in order to write your own animations, and which are applicable to many other aspects of app development,
- Give you the source code with inline commentary which produces the animation shown above,
- Show you the console output resulting from running my source code,
- Ask you some questions about the iOS block animation protocol, and
- Leave you with some final thoughts on how we’ll wrap this series up.
- UIView bounds,
- UIView frames,
- UIView geometry,
- the Core Graphics framework (and Quartz to some extent),
- a storyboard,
- a storyboard scene,
- a UIViewController in code,
- subclassing a UIViewController in code,
- a UIViewController in a storyboard scene,
- the backing UIViewController subclass for a a UIViewController in a storyboard scene,
- points versus pixels,
- a CGRect,
- Auto Layout — especially understanding basic constraints,
- blocks/closures, and,
- threading/concurrency.
- What’s the difference between a UIView’s bounds and frame?
- How do you subclass a UIViewController in code?
- What is subclassing?
- What is inheritance, polymorphism, and encapsulation?
- How is a UIViewController in a storyboard scene tied to its backing UIViewController subclass in code?
- What is the difference between points and pixels?
- What is a CGRect and how is it related to a UIView?
- What is an Auto Layout constraint and how do you set enough of them to position one UIView?
- What is a “block” or “closure?”
- What is threading/concurrency?
- On which thread do you update the user interface?
1. Where are we in this series?
In this series of posts, “Basic animation, Auto Layout, and view geometry – Part X,” we’re learning about basic animation in multiple steps. In the first post on Monday, “Basic animation, Auto Layout, and view geometry – Part 1,” we covered setting up a storyboard scene using Auto Layout. In Tuesday’s post, “Basic animation, Auto Layout, and view geometry – Part 2,” we used some basic UIView geometry to play with shapes and sizes and we drew on the iPhone screen. On Wednesday (“Basic animation, Auto Layout, and view geometry – Part 3”), we wrote the code to perform a simple block animation. I started writing code Wednesday in Swift 3.0.
2. Required concepts for UIView development, including animations
You absolutely must know about these iOS concepts to become a great iOS developer:
3. View geometry
Let me show you some diagrams defining geometric concepts you must learn in order to write your own animations, and which are applicable to many other aspects of app development. Watch the animation above and look at these two diagrams.
4. Source code for creating, configuring, and manipulating UIView’s programmatically
Here’s my source code which produces the animation shown above. Read the inline commentary. Read the code. Use reference materials to look up anything you don’t understand.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
// // ViewController.swift // Animation Demo // // Created by Andrew Jaffee on 1/7/17. // Copyright © 2017 microIT Infrastructure, LLC. All rights reserved. // import UIKit class ViewController: UIViewController { // our main drawing view ("canvas" if you like); this is the // one we created in the storyboard @IBOutlet weak var mainSubView: UIView! // the view we'll animate; to keep the code simple, I'm // forcibly unwrapping this view instead of using an optional or // creating an init for the UIViewController var animateView: UIView! // to make the animatable view look nice, we offset it // from the main canvas view let animateViewMarginToMain: CGFloat = 10.0 // this is the initial width and height of the // view we'll animate; initially, it is relatively small compared // to the canvas view let animateViewWidth: CGFloat = 50.0 let animateViewHeight: CGFloat = 50.0 // instantiate the variables that will define a // new width and height for the subview we'll animate var animateViewNewWidth: CGFloat = 0.0 var animateViewNewHeight: CGFloat = 0.0 // MARK: - User interactions /** When the user taps the "Animate" button, we... animate the initially small view that we added to the canvas view in viewDidLoad */ @IBAction func animateButtonTapped(_ sender: Any) { // I want the initially small view to grow until it almost fills // the canvas view; I keep the margins of the subview the same // on top, bottom, right, and left. animateViewNewWidth = mainSubView.bounds.width - animateViewMarginToMain*2.0 animateViewNewHeight = mainSubView.bounds.height - animateViewMarginToMain*2.0 // display the calculation for making the subview grow but staying // within the canvas view, with a nice consistent margin print("animateViewNewHeight/Width: \(mainSubView.bounds.width) - \(animateViewMarginToMain*2.0) = \(animateViewNewWidth)") // show the bounds and frame of the canvas view and animatable subview // BEFORE the animation printCGRects() // "Animate changes to one or more views using the specified // duration, delay, options, and completion handler." UIView.animate(withDuration: 2.0, delay: 2.0, options: UIViewAnimationOptions.curveEaseIn, animations: { // This is the FIRST animation. This will make the subview // appear -- fading into visual appearance self.animateView.alpha = 1.0 }) { (completed:Bool) in // This block is executed after the FIRST animation completes. if (completed) // Only proceed if the FIRST animation succeeds. { // This is the SECOND animation block. UIView.animate(withDuration: 2.0) { // According to Apple, the frame is one of the "Animatable UIView properties" // and we can "Modify this property to change the view’s size and position // relative to its superview’s coordinate system." self.animateView.frame.size.width = self.animateViewNewWidth self.animateView.frame.size.height = self.animateViewNewHeight // show the bounds and frame of the canvas view and animatable subview // AFTER the animation self.printCGRects() } } // end if } // end completion block } // end func animateButtonTapped // MARK: - UIViewController delegate override func viewDidLoad() { super.viewDidLoad() // Create a subview in our main drawing view on app startup configureAnimateView() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } // end func viewDidAppear // MARK: - Subview setup /** Create a subview in our main drawing view; initially, hide the subview */ func configureAnimateView () -> Void { // Create the CGRect which defines the geometry of the subview to animate // DO I NEED TO ADD THE mainSubView.bounds.origin.x AND y HERE??? let animateViewRect = CGRect(x: animateViewMarginToMain + mainSubView.bounds.origin.x, y: animateViewMarginToMain + mainSubView.bounds.origin.y, width: animateViewWidth, height: animateViewHeight) // Create the subview based on the CGRect animateView = UIView(frame: animateViewRect) // Make the subview red so it is easily identifiable in animation animateView.backgroundColor = UIColor.red // Initially hide the subview so that we can animate it // onto the main canvas view animateView.alpha = 0.0 // WE MUST add the new subview to the main canvas view mainSubView.addSubview(animateView) } // end func configureAnimateView // MARK: - Helper method(s) /** Display bounds and frame definitions for views we're working with */ func printCGRects() -> Void { print("----------------------------------------") print("mainSubView frame: \(mainSubView.frame)") print("mainSubView bounds: \(mainSubView.bounds)") print("animateView frame: \(animateView.frame)") print("animateView bounds: \(animateView.bounds)") print("----------------------------------------") } /** Remove all subviews from main drawing/animation view */ func removeSubviews() -> Void { for subView in mainSubView.subviews { subView.removeFromSuperview() } } } // end class ViewController |
5. The output to console resulting from running my code
1 2 3 4 5 6 7 8 9 10 11 12 13 |
animateViewNewHeight/Width: 240.0 - 20.0 = 220.0 ---------------------------------------- mainSubView frame: (67.5, 213.5, 240.0, 240.0) mainSubView bounds: (0.0, 0.0, 240.0, 240.0) animateView frame: (10.0, 10.0, 50.0, 50.0) animateView bounds: (0.0, 0.0, 50.0, 50.0) ---------------------------------------- ---------------------------------------- mainSubView frame: (67.5, 213.5, 240.0, 240.0) mainSubView bounds: (0.0, 0.0, 240.0, 240.0) animateView frame: (10.0, 10.0, 220.0, 220.0) animateView bounds: (0.0, 0.0, 220.0, 220.0) ---------------------------------------- |
6. Questions about the iOS block animation protocol
Can you answer these questions?
7. Final thoughts
Oy… that was a lot of information. Are you up to the task? Will you think about my questions? Can you answer them? Do you need to do some research to answer these questions? In the next post, we’ll tie it all together. Thanks! (And remember to leave comments if you have questions and/or comments.)