Prototyping with Xcode
Part 3
In the second part of our Prototyping with Xcode series, we made our prototype useful with working navigation. Now we’ll see about bringing it to life with a little animation.
Reminder: This tutorial requires Xcode 6.4 or above. Xcode 7 works too.
Get Spring
We’re going to download a third-party animation library and add it to the project.
- Go to Spring project page.
- Click on “Download ZIP.”
- Locate and expand the downloaded zip archive on your hard drive.
- In the expanded folder “Spring-master,” there is a folder simply named “Spring.” Drag the Spring folder into your Project organizer.
- When Xcode asks about adding the files, check “Copy items if needed” and “Create groups.”
That’s really all you have to do to start using animation in your prototype. The Spring library provides a number of classes that are just animation-ready variations on object types you’re already using. For UIImageView, Spring provides SpringImageView; UIButton becomesSpringButton; UITextView becomes SpringTextView; and UILabel becomes SpringLabel. Making a component of your prototype animatable is now just a matter of reassigning it to the appropriate Spring class.
Assign Classes
Switch to Main.storyboard.
- In the first view controller (the Food Status view), select the date label.
- In the Identity inspector (right pane, third icon), type “SpringLabel” in the Custom Class: Class field.
- Select the text field and assign it a class of “SpringTextView.”
- Assign the Post Status button a class of “SpringButton.”
- In the Choose Image view controller, select each image button and assign it the “SpringButton” class as well.
Assigning these Spring classes grants each component a new set of properties in the Attributes inspector. Select the date label in the Food Status view and switch to the Attributes inspector to see these.
There’s a lot of fine-tuning available to you, but for now we’re only interested in seven properties:
- Autostart determines whether a component will begin animating immediately. You will usually want to set this to “On.” The reason to set it to “Off” would be if a developer wants to trigger the animation from code.
- Autohide controls whether a component will set itself to zero opacity prior to animating. This is only relevant for fade-in animations. For those, you will want to set this to “On.”
- Animation is the name of the animation type you want for the component. You must type in the exact name of the animation. Your options here are: slideLeft, slideRight, slideDown, slideUp, squeezeLeft, squeezeRight, squeezeDown, squeezeUp, fadeIn, fadeOut, fadeOutIn, fadeInLeft, fadeInRight, fadeInDown, fadeInUp, zoomIn, zoomOut, fall, shake, pop, flipX, flipY, morph, squeeze, flash, wobble, and swing.
- Delay is how long the component should wait to play its animation.
- Duration is how long the animation should take.
- Scale X and Scale Y are used to set an item’s initial size if you want the item to grow or shrink toward its normal size as it animates.
Setting Animations
We’re going to make the components of our Food Status view sweep into position using the fadeInLeft animation and varying delays.
With the date label selected, perform the following in the Attributes inspector:
- Set Autostart to On.
- Set Autohide to On.
- Use “fadeInLeft” for Animation.
- Set Delay to 0 (or leave empty).
- Set Duration to 0.75.
For the text field, do the following:
- Set Autostart to On.
- Set Autohide to On.
- Use “fadeInLeft” for Animation.
- Set Delay to 0.1.
- Set Duration to 0.75.
For the Post Status button, do the following:
- Set Autostart to On.
- Set Autohide to On.
- Use “fadeInLeft” for Animation.
- Set Delay to 0.2.
- Set Duration to 0.75.
If you’re unable to add animation attributes to any item, make sure its class is set correctly in the Identity inspector.
Run the app. You should see a staggered “wave” of components as the UI sweeps in from right to left.
Return to the storyboard. We’ll add animations to the image buttons in the Choose Image view as well. For each image button, do the following:
- Set Autostart to On.
- Set Autohide to On.
- Use “fadeIn” for Animation.
- Set Duration to 2.
- Set Scale X to 0.9.
- Set Scale Y to 0.9.
Here again we want to offset the delay for a staggered effect. Assign the first button a delay of 0.5, assign the second 0.75, and the third 1. Run the app to see the results.
If you’ve followed this series from the beginning, you now know enough to create some pretty impressive native prototypes with Xcode. There’s no need for you to read further. What follows are two more advanced tips: making the status bar behave, and chaining app behavior to animation.
(If this is where you get off, you can download the example project file. If you’re using Xcode 7.1, download this version instead.)
Wrangling the Status Bar
For the sake of getting our prototype up and running quickly, we cheated a bit on implementing our navigation bar. If you’ve seen the background image awkwardly showing through the status area, that’s why.
Rather than add the navigation bar to the main view controller directly, the more proper way to build the view is to embed it in a Navigation Controller. To do that, select the Food Status view controller (click the frame at the top or the yellow proxy icon in it) and use Editor > Embed In > Navigation Controller. Select the navigation controller that appears and set its Size to “iPhone 4.7-inch” in the Attributes manager.
Select the Navigation Bar in the navigation controller — you may have to use the structure pane so as not to select the whole controller — and do the following:
- Change Style to Black (this will produce white title text).
- Uncheck Translucent.
- Change Bar Tint to HSB 30º, 70%, 70%.
Our Food Status view controller will now appear to have two navigation bars. We’re going to delete the lower bar, but first drag the camera image bar button item into the bar above. Double click the middle of the new upper bar and type “Food Status.” Then, delete the old navigation bar.
Unfortunately, auto layout constraints will be a mess after these changes. You’ll need to adjust the position and constraints for the background image view, date label, and text field. Be careful to constrain elements to the container view rather than the background image view. Use the method of control-dragging between items in the structure pane as needed.
Since the Choose Image view is being presented modally, we can leave its navigation bar as-is.
To create a more typical view-to-view drill-down transition, you would link from the first view controller to a second with the “Show (e.g. Push)” segue. However, don’t try to perform a Show segue from within a navigation controller to another navigation controller. That will cause the app to crash.
Once you’ve corrected or rebuilt your constraints, you’ll have navigation and status bars that work consistently on all devices. However, you probably want to change the status bar icons and text to white in order to stand out from our earthy background color.
Status Bar Style
For this, we need to make some changes to the app’s property list. In the Project navigator, look for the folder named “Supporting Files.” Click the disclosure triangle to expand its contents, and select the file Info.plist.
We’re going to add two “keys” to the property list that will determine how the status bar displays. Roll over the “Information Property List” header and look for an Add button to appear. Click that button. Type “Status bar style” and select the matching item in the list.
Now, in the Status bar style row of your property list, look at the far right end of the Value column for a popup button. Click it and select the “Transparent black style” value.
To be safe, click the Add button again and look for a key with the name “View controller-based status bar appearance.” Add it and set its value to “NO.”
It’s possible to set the Status Bar style in the Attributes inspector when a view controller is selected. Since we don’t want the style to vary from view to view, we add this second key as a global override.
Run the app and enjoy the improved status bar.
Chaining actions and animations
When prototyping, you may want an action (such as a segue) to take place only once an animation has finished. The Spring library makes this pretty easy, if you don’t mind writing some Swift code.
Let’s try making the buttons in our image chooser view display a quick bounce before routing the user back to the main view:
- Return to Main.storyboard.
- Select one of the breakfast image buttons in the image chooser view.
- Switch to the Connections inspector (sixth icon in the right-hand pane).
- Under “Triggered Segues” is a widget indicating we’ve connected this button to unwindToFoodStatusViewController. Click on the X button in the widget to delete the connection.
Repeat this process for the other two image buttons. Do not delete the unwind connection for the Done button. We’re going to reuse that in code.
Identify the Unwind Segue
We can actually select the unwind segue on the storyboard, though we have to do it via the structure pane. Look for the “Unwind segue to Scene Exit Placeholder” item in the second view controller’s hierarchy. Select it and then look at the Attributes manager.
There’s an Identifier field that allows us to name the segue. Type in “UnwindToFoodStatus.” We need to be able to respond to button presses in code, so we’re going to create some Interface Builder outlets between our storyboard and the view controller subclass we’ll now create.
Select File > New > File. Choose iOS > Source > Cocoa Touch Class. Hit Next. Provide a class name of “ImageChooserViewController.swift” and set it to be a subclass of UIViewController. Leave the language as Swift. Hit Next and save the new file in your project’s folder. You can delete the view controller boilerplate code (everything between the opening and closing curly braces).
Switch back to the storyboard and select the image chooser view controller itself (click its yellow proxy icon if you want to be sure), and then assign it a class of “ImageChooserViewController” in the Identity inspector.
Now, with the same view controller still selected, click the Show Assistant Editor toolbar button (or use View > Assistant Editor > Show Assistant Editor). Xcode should open a new pane containing the file relevant to your selected controller: the ImageChooserViewController.swift file we just created.
If the Assistant opens any other file, you can correct it by dragging ImageChooserViewController.swift from the Project navigator into the breadcrumb bar at the top of the Assistant pane. Don’t drag the file into the code editor though, as Xcode will think you want to reference it in the code.
Scroll the storyboard and adjust the Assistant pane width (drag its left edge) until you can see both the image chooser view controller and ImageChooserViewController.swift at the same time.
Create Action Outlets
Now, for each of the three breakfast image buttons in the view, do the following:
- Control-drag from the button to ImageChooserViewController.swift, releasing the mouse just under the line which reads:
class ImageChooserViewController: UIViewController {
- In the popup that results, change “Connection” to “Action” and give the image button a unique name (you might use Breakfast1, Breakfast2, and Breakfast3).
- Click the Connect Button.
Repeat this process until all three image buttons are connected to ImageChooserViewController.swift with unique names.
Any code we add within the braces of a button’s @IBAction function will trigger when that button is clicked. Since each button is going to do the same thing in this prototype, there’s no need to write it three times. Instead, we’ll nest a new function inside.
The Animation Function
Below the curly brace of the last @IBAction function, but above the final closing brace of the ImageChooserViewController class, add the following code:
func animateButton(referringButton: AnyObject) {
}
This is a new function, animateButton, that expects to be supplied with an argument — the (referringButton: AnyObject) — when called.
We could change referringButton to any name we like. It’s there to hold a reference to the button object that’s calling the function. We know the referring button will be of the type SpringButton, but we’re telling Xcode we’ll accept any kind of object (the AnyObject part) for now.
Now, when one of our image buttons calls the animateButton function, it will need to supply a reference to itself so we know which button was triggered. Our @IBAction functions already capture this information, thanks to the parameter (sender: AnyObject) Xcode inserted. We just need to hand off that sender as the referringButton.
Within the curly braces in each of the IBAction functions, add the following code:
animateButton(sender)
This will cause our buttons to refer themselves to the animateButton function when clicked. In the animateButton function braces, add the following:
var sendingButton = sender as! SpringButton
The whole function now looks like so:
func animateButton(referringButton: AnyObject) {
var sendingButton = referringButton as! SpringButton
}
To quickly break the new line down, we’re creating a variable (var) named sendingButton to hold the reference we received as referringButton. We know the referringButton will be of the type SpringButton, so we “insist” on this fact using as!. If we don’t establish that sendingButton is a SpringButton, Xcode won’t let us add any animation properties to it.
We could do this with a little less code if in each IBAction function we change (sender: AnyObject) to (sender: SpringButton). This would render the var sendingButton… conversion unnecessary. If you want to go this route, delete that line; but note that any reference to sendingButton in the rest of the tutorial will need to be replaced with referringButton.
sendingButton is now a stand-in for the SpringButton that the user clicked. We can add animation properties to it in code in much the same way we did so using the Attributes inspector previously. Let’s give sendingButton a “squeeze” animation with no delay, a 0.75 second duration, and a starting scale of 90%.
sendingButton.animation = “squeeze”
sendingButton.delay = 0
sendingButton.duration = 0.75
sendingButton.scaleX = 0.9
sendingButton.scaleY = 0.9
At this point we could trigger the animation with SpringButton’s built-in animate function using sendingButton.animate(). But we don’t only want to animate the button. We want the button to animate, trigger our unwind segue, and return the user to the initial view.
Trailing Closure
The Spring library provides for this possibility as well with a function named animateNext. This lets us trigger the animation, and then follow up the animation with something else. When a function provides a way to signal its completion, we call it a “completion handler.” We make use of the completion handler by adding something called a “trailing closure.” A trailing closure can be as succinct as this:
{ () in }
Anything we add after the in but before the second curly brace will be performed once the function has completed. As we said, the thing we want to happen after the animation is trigger our unwind segue. Since we gave that segue a name of UnwindToFoodStatus on the storyboard, we can call it like so:
self.performSegueWithIdentifier(“UnwindToFoodStatus”, sender: self)
Putting this together with animateNext and our trailing closure, we get:
sendingButton.animateNext { () in
self.performSegueWithIdentifier(“UnwindToFoodStatus”, sender: self)
}
Our complete animateButton function now looks like this:
func animateButton(referringButton: AnyObject) {
var sendingButton = referringButton as! SpringButton
sendingButton.animation = “squeeze”
sendingButton.delay = 0
sendingButton.duration = 0.75
sendingButton.scaleX = 0.9
sendingButton.scaleY = 0.9
sendingButton.animate()
sendingButton.animateNext { () in
self.performSegueWithIdentifier(“UnwindToFoodStatus”, sender: self)
}
}
Because each of our image buttons calls this same function, they should all behave correctly. Run the app and see what happens when you click an image in the image chooser view.
You now know quite a bit about working with Xcode. You can build a storyboard with autolayout, connect elements to code, and even perform animation. Xcode isn’t always the best choice for prototyping a new app, but hopefully you’re now comfortable enough with it for those times when it is.
(You can download the advanced example project file here. If you’re using Xcode 7.1, download this version instead.)