☝️ NO submission is required for labs
MarioKart - Gestures & Animations
Tired of boring button-centric UI? Well…in iOS it's easy to implement interactive gestures and fun animations to give your UI some well deserved pop! In this lab you'll build an app that allows users to interact with characters from the iconic video game, Mario Kart, panning, scaling, rotating and then sending them zooming off the screen! 🏎
The user stories for this lab are split up into 3 tiers. Tier 1 stories will introduce the core concepts of working with gestures and animations. Your goal should be to get through Tier 1 stories during your in-class lab time.
After completing Tier 1 stories you'll be able to…
Tier 2 & 3 stories build on the core concepts from tier 1 and yield a more complete and nuanced app. Try out Tier 2-3 outside of class if you really find gestures & animations intriguing or go back to them later if you want to include some of these features in your group project app.
In this user story, we'll leverage a pan gesture recognizer and it's location property to move around the position of our karts.
Add the image assets to your project
app_icon.png
to the Assets.xcassets
-> AppIcon
-> iPhone App @2x
kart_[x]@2x.png
images and background@2x.png
image you downloaded to your Assets.xcassets
folder.
The @2x
in the image file name helps Xcode place the file in the correct resolution slot automatically.
Layout your views
Access the Media Library by long clicking
on the Object Library icon (see gif below) or use the quick key: command
+ shift
+ m
You can duplicate views by holding option
while you click and drag.
Add pan gesture recognizers for kart image views.
Create and connect actions for your pan gesture recognizers.
Creating an action will trigger a method to be called anytime your gesture recognizer recognizes a gesture.
control
+ drag
ing from a gesture recognizer to your view controller swift file to create an action and associated function. You can name the function something like: didPanKartView
⚠️ Drag from the gesture recognizer listed in the Document Outline, NOT from the image view in the storyboard. (See example below)
⚠️ Make sure you set the type to UIPanGestureRecognizer when creating the action. (See example below)
control
+ drag
ing from from each one to the same function you created in the first action.
Connecting all of the gesture recognizers to a single function allows us to reuse our pan logic for each kart view. This is especially useful if we want to add more karts in the future.
Code the logic to move the kart when it's panned.
Access the location property of the pan gesture recognizer.
^^^
Where should I write this code?
^^^
Any code you want to run during a panning event should be in the body of the function (action) you created for the gesture recognizer.
^^^
^^^
What's sender
?
^^^
When a gesture recognizer is triggered and calls it's associated function (action), it includes itself as the sender
. When you reference the sender
in the body of the function, you are referencing the specific gesture recognizer that was triggered. This is how you access properties of the gesture recognizer like it's location
on the screen as well as the view
it's attached to.
^^^
^^^
What's location
?
^^^
location
is a property of pan gesture recognizer's that tells us where the the user has panned in some area that we specify. In this case, it's the location in reference to the entire screen , aka the view
. Positions of views are described using a data structure called CGPoint
, which consists of an x
and y
coordinates.
^^^
^^^
What's view
?
^^^
All view controllers come with a root view called view
at the top of the view hierarchy. This is the main view that we are adding all of our other views into. In the above line, we are asking for the pan gestures current location within the root view.
^^^
Print the current location returned from the gesture recognizer.
📲 RUN YOUR APP and pan on each kart. You should see the position of the gesture recognizer printed out in the console as you pan your finger.
Notice how…
Access the view of the kart that was panned.
Each gesture recognizer knows the view
it's attached to. We can ask the gesture recognizer (sender
) for it's view in order to access the specific view that was panned (i.e. which kart image view).
view
property of the sender
using a !
to avoid having to continuously account for it as an optional
value. In our case, it's safe to do so since we can be assured that anyone calling this method will have a view attached and not be nil
.Set the kart view's position to the current position of the gesture recognizer.
What's center
❓ - All views have a center
property which describes the point of their position. Like the position
property of the pan gesture recognizer, the center
property is a CGPoint
with values for x
and y
coordinates. The position of the center is in reference to the view that contains the view, this is called the super view.
📲 RUN YOUR APP and see if you can move your kart!
Most of the concepts you applied to get the karts panning will be used in a similar way to scale the karts using a pinch gesture. The main difference is that we will be referencing the pinch gesture's scale property to scale our karts up and down.
Add pinch gesture recognizers to kart views.
Create an action for a pinch gesture recognizer and connect the remaining.
control
+ drag
from a pinch gesture recognizer in the Document Outline to create an action in your view controller swift file.didPinchKartView
ctrl
+ dragging
them each to the same action you created for the first pinch gesture recognizer.Access the scale property of the gesture recognizer that was pinched.
Similar to the pan gesture recognizer's location
property, pinch gesture recognizers have a scale
property that corresponds to the size of the user's pinch.
Print the scale value to the console
How do you pinch on the simulator?
• Hold down the option
key and you'll see two gray circles appear. Those represent the user's fingers.
• Move the cursor while continuing to hold the option
key until the circles are close together.
Now, Additionally hold down the shift
key and move the two circles over the object you want to pinch.
• Release the shift key, while continuing to hold the option
key, click
on the object you want to pan and (while continuing to hold the click) move the cursor to pinch in and out.
📲 RUN YOUR APP and pinch on each kart. You should see the pinch value of the gesture recognizer printed out in the console as you pinch.
Notice how…
• Pinching on any of the karts calls our pinching function.
• The pinching function is called continuously during a panning event.
• Wherever the pinching starts corresponds to a scale value of 1
Access the view of the kart that was panned.
Adjust the scale of the kart view using the scale from the pinch gesture recognizer.
All views have a transform
property which, among other things, allows you to modify the view's scale rotation and translation. The transform
property is of type CGAffineTransform
, which isn't really too important for us besides helping us navigate to handy constructors to make a new transform with whatever modifications we'd like, such as the scale. In the above line, we create the transform with the scale value we get from our pinch gesture recognizer. We'll plug the scale value in for both x
and y
to get a uniform scale in both width and height.
📲 RUN YOUR APP and pinch to scale your karts up and down!
Rotating the kart using a rotation gesture is going to be almost identical to scaling using a pinch. By this point you're really getting the hang of gestures so this should be a synch!
Add rotation gesture recognizers to kart views.
Create an action for a rotation gesture recognizer and connect the remaining.
control
+ drag
from a rotation gesture recognizer in the Document Outline to create an action in your view controller swift file.didRotateKartView
ctrl
+ dragging
them each to the same action you created for the first.Access the rotation property of the gesture recognizer that was rotated.
Similar to the pinch gesture recognizer's scale
property, rotation gesture recognizers have a rotation
property that corresponds to the amount of rotation in the user's gesture.
Print the rotation value to the console
📲 RUN YOUR APP and rotate on each kart. You should see the rotate value of the gesture recognizer printed out in the console as you rotate.
• As with all gestures, an actual device is the preferred way to test.
• When using the simulator, the rotation gesture works similar to the pinch gesture, only instead of moving the circles (fingers) in and out, you're moving them in a circular motion.
• Our current setup only allows for one gesture recognizer to work at a time. So, if you make a pinch motion before your rotation, the pinch gesture recognizer will claim the gesture event and the rotation gesture will not be triggered.
• The rotation values don't seem to be in degrees 🤔 They're not…they're in radian! Silly engineers…🤓
Access the view of the kart that was panned.
Adjust the rotation of the kart view using the rotation from the pinch gesture recognizer.
Similar to the approach we used to modify the scale, we'll create a new transform using one of CGAffineTransform
s handy constructors which takes an angle (in radian). We'll then set the transform
property of the view to our rotated transform to rotate the view.
📲 RUN YOUR APP and rotate your karts around and around!
This story will incorporate view animations that will be triggered using a tap gesture recognizer. Incorporating the tap gesture will be almost identical to the last user stories.
Add tap gesture recognizers to kart views.
Create an action for a tap gesture recognizer and connect the remaining.
control
+ drag
from a tap gesture recognizer in the Document Outline to create an action in your view controller swift file.didTapKartView
ctrl
+ dragging
them each to the same action you created for the first pinch gesture recognizer.Set the number of taps required to 2
.
Tap gesture recognizers can be configured to respond to different numbers of taps. You can also configure the number of touches which is how many fingers the user is required to use during the gesture.
Test the tap gesture recognizer.
📲 RUN YOUR APP and double tap on each kart. You should see the test statement print out in the console.
Access the view of the kart that was panned.
Test moving the kart's position
The karts move 50pts on the x-axis, however the movement is more like teleportation then smooth motion. We'll fix that next by using a view animation method.
📲 RUN YOUR APP and see if the karts move!
Animate the movement of the kart's position
View animation is a breeze using one of several animation methods available in the UIView
class. We'll start with the simplest version which allows you to set the duration of the animation and set the end state of the view your animating.
() -> Void
closure is highlighted to expand it and reveal it's body.📲 RUN YOUR APP and test out your animation!
Tune your race animation!
Now that you can animate your karts, it's up to you to tune the values to get the perfect race effect
📲 RUN YOUR APP and your off to the races! 🏎
Our app has come a long way! We can move, scale, rotate and race our karts off the screen…the only problem is getting them back. In this final tier 1 user story, we'll add a long press gesture to trigger a kart reset.
Store the starting position of the karts.
In order to reset the karts to their original position, we'll need to know where to reset them to. We can store the position of the karts when our app first loads for reference.
We'll want to access these properties in several places in our app, so declare them in a broad scope, i.e. in the same place we created our outlets.
The viewDidLoad method is a great place for any initial setup you need to do before your view controller is presented. As the name suggests, this method is called once all of the view controller's views have been loaded, so it's safe to reference properties from our kart views.
Add a long press gesture recognizer to the background image view.
Create an action for the long press gesture recognizer.
control
+ drag
from the long press gesture recognizer in the Document Outline to create an action in your view controller swift file.didLongPressBackground
Code the logic to reset the kart positions
📲 RUN YOUR APP and see if you can long press to reset your karts.
Animate the resetting of karts.
The current resetting of the kart's is bit abrupt. Let's wrap the resetting logic in a view animation method to smooth it out.
🛑 Reference to property startingPointKartView0
in closure requires explicit self
. to make capture semantics explicit…Insert self
.
self.
before each object yourself.
The requirement of self
in this case has to do with unique properties of closures in Swift and is not important for our purposes at this point.
All you need to know is…
Anytime you are working with animations and you get an error instructing you to add self.
…JUST DO IT! 👟
📲 RUN YOUR APP to see the karts animate as they reset their positions.
Reset karts to their unmodified states
The karts reset to their starting positions just fine, however their transforms are not being reset which is leading to odd behavior if a user has scaled or rotated a kart.
To reset a transform, we just need to create a new unmodified transform and assign it to the view we want to reset, which in this case is our kart views. You can create an unmodified transform using the transform identity
property.
📲 RUN YOUR APP and see if scaled or rotated karts animate back to their unmodified states.