Apps with UIKit - Learning Swift Programming (2015)

Learning Swift Programming (2015)

12. Apps with UIKit

Apple has two major kits available in its library: UIKit and AppKit. AppKit is for desktop applications, and UIKit is for iOS applications. In this chapter you are going to explore UIKit and take a look at how to build some common applications.

UIKit has gotten much easier to use in this iteration of iOS. While you can write everything from scratch in code, it’s much easier to use the storyboard. Thanks to the storyboard, things like size classes mixed with constraints make automatic layout of your application super easy. A lot of the applications that you see in the App Store and applications that you’ve used every day are built using these utilities. You can customize these UI tools as much as you need to.

In this chapter, you will learn about a couple of the most common tools and how to use them. Each tool is meant to be used in a specific way. This means that Apple expects you to write your code in a way that fits its design guidelines. At first, these coding guidelines might seem complicated, but you will eventually realize how nice it is to have a plan of attack for each situation that arises. Unlike with other frameworks, Apple has design guidelines that are meant to be followed. This doesn’t mean you can’t step outside those guidelines; in fact, doing so might be what makes your app unique. You can use these user interface designs to your advantage to build the app that you want, quickly and painlessly. The first couple times you create a table or a search, you might think about the complexities of this design. After doing it 30 or 40 times, you’ll be able to do it with your eyes closed. And there’s nothing wrong with creating a custom user interface that you feel is appropriate. Just don’t use undocumented functions or Apple will probably reject your app.

Application Types

Each app gets a storyboard, which is named main.storyboard. This is basically a giant XML file that describes the app’s layout. In this chapter, you’ll learn how to implement these common user interface designs by using Swift. To see how it works, you need to create a new project by selecting File > New > Project. At this point, you should be presented with a screen that looks like the one shown in Figure 12.1. The following sections describe the application types shown in this figure.

Image

Figure 12.1 Choosing your application starting point.

Single-View Applications

When you start with a single-view application, Xcode starts you off with just a simple UIView, the most basic view available. A lot of other views inherit from this view. To choose this kind of application, click Single View Application and then click Next. Name your project single view, make sure Language is set to Swift, and make sure the Device is Universal. When you open the Main.storyboard file, you see the view shown in Figure 12.2.

Image

Figure 12.2 The main.storyboard GUI layout.

In Figure 12.2, note the couple of labels added to the storyboard:

1. In this sidebar area, you can make changes to things that are currently selected in the main area (#3). Most importantly, you have that toolbar of six icons (right next to the #1). I refer to this as the sidebar toolbar. Hover over each icon to see its name (currently the Identity inspector is selected).

2. Each view has an accompanying ViewController (which controls the view, as you would expect). This Swift file can also control the stuff on this screen. To select a ViewController and get options for its context in the sidebar area, click the little yellow icon with the white square in it (next to #2). I refer to this as the view controller button.

3. This is the view itself, which is currently just a plain view with nothing in it. Notice the arrow pointing at it on the left. This means that this is the first view. When your app starts, this is the first view that pops up.

4. This is the component area, where you can add more views by dragging them onto the screen as well as other user interface controls, like buttons and labels you want to put in your view.

To examine the classes this view is made of, click the view (#3 in Figure 12.2). It sometimes seems like nothing happens when you click the view, but don’t worry: clicking really does select it. Now select the Identity inspector from the sidebar toolbar (the third icon from the left). You should see that the class for this view is a UIView (see Figure 12.3).

Image

Figure 12.3 The Identity inspector.

This means that if you want to program things in Swift for this view, you will be dealing with the UIView class. In order to control UIView, you use a UIViewController. So you need to determine which UIViewController is currently controlling this UIView. Click on the view controller button (next to #2 in Figure 12.2). In the sidebar toolbar, make sure the Identity inspector is selected again. As shown in Figure 12.4, you see that the class assigned to the view controller is called ViewController.

Image

Figure 12.4 Renaming the class in the Identity inspector.

This class is currently in your project. If you look in your list of files, you will see a file called ViewController.swift. You can either open it there by clicking the filename or by clicking the arrow next to the word ViewController in the dialog shown in Figure 12.4. After you open the file using either method, you should notice that this class inherits from UIViewController. Now you know that if you want to control UIView by making your own view controller, you must create a class that inherits from UIViewController. You should see something like this:

class ViewController: UIViewController {

If you were to create a new file and in that file you created a class that inherited from UIViewController, then in that drop-down in Figure 12.4 you would see that class. You could then assign that custom class as the view controller for your view. Although Xcode automatically generates all the code you need to get your project started, you now know how to create a project from scratch.


Note

Don’t forget that any changes you make in your storyboard must be saved, as the storyboard is just like any other regular file.


Go back to the storyboard and add a button on the screen. Go down to the section labeled #4 in Figure 12.2. In the search box, type the word button. When a button comes up for this search, you can drag it onto your view. If you hover the button around the center of the view, you should be able to get it locked in on center, both horizontally and vertically. (Note that this does not guarantee that your button will stay in the center when the device is rotated. You would need to add constraints in order for that to work.) After you place the button onto the screen, click the view controller button, and in the top-right corner (second from the left of six buttons) you should see a button that looks like a tuxedo (it may also be two circles in Yosemite (the new Apple operating system); either way it is called the assistant editor and it is second from the left). Click this button (see Figure 12.5).

Image

Figure 12.5 The assistant editor is selected.

Clicking the tuxedo button brings up the code that accompanies your view controller. You should now have a split view of the storyboard and the ViewController.swift file. You want to be able to control the button that you created from the code. Therefore, you need to create a reference to the button in the code as well as an action for the button in the code. In order to do this, Control+drag the button from the storyboard over to the ViewController.swift file (see Figure 12.6).

Image

Figure 12.6 Creating a reference in the code by Control-dragging.

In the dialog that pops up, for Connection choose Action. This allows you to respond to the button being clicked by running your own function. In the Name field type buttonClicked. This will be the name of the function that Xcode will create. Click Connect. Xcode creates a function that looks like this:

@IBAction func ButtonClicked(sender: AnyObject) {
}

It’s important to note that just because you created this function does not guarantee that everything is connected properly. If you did everything as instructed, of course it should work, but sometimes things get messed up when you experiment and don’t do it right the first time. It’s important to know for sure that things are either connected properly or not. So take a look: In the storyboard click the button to select it. In the sidebar toolbar, click the arrow button (the one farthest to the right) to show the Connections inspector. If you have the button selected, you can see all the events that should be triggered when that button is acted upon. You should see an event listed for Touch Up Inside (see Figure 12.7). If you want to cancel the connection, you can click the X in that area.

Image

Figure 12.7 Viewing your connections.

This means that your button (when touched) is going to look for a method called buttonClicked. In the code, you can now respond to the button being clicked. For now you can just print something when the button gets clicked. Change your methods to look like this:

@IBAction func buttonClicked(sender: AnyObject) {
println("Button was clicked!")
}

Now you can run the program and click the button. Try it to see if it works. When you run the program in your console, you will get the message from the function. The button is horribly positioned, however. You can quickly fix that by adding some constraints to the button.

Adding Constraints

By adding constraints, you allow the compiler to automatically calculate the position of your controls. When you first add constraints, the drawn line of the constraint may be orange, or it may be blue. It’s important to know that when the line is orange, it means that you have not given Xcode enough information to calculate the constraint fully, and therefore it cannot properly position your item. It is not until your constraints turn blue that you can be sure that they are going to work.

To add a simple constraint to your button, Control+drag the button to the left and let go (in the whitespace area). A dialog appears, allowing you to choose a constraint. Choose Center Vertically in Container. You should now have an orange line going horizontally across the button. (Remember that orange means that the constraint is not totally set yet.) Add one more constraint by Control+dragging from the button up. A dialog appears, allowing you to choose a constraint. Choose Center Horizontally in Container. Your previous constraint should now turn blue, and you should have a new constraint going from top to bottom that is also blue.

You can now run your program once more, and the button should be perfectly centered.

Adding a New View

To improve your app, you can add another view that this view will transition to. Before you do anything else, though, change the label of the button to say Go. In your component search, look for and add two text fields and a label. Place the two text fields, spaced apart, below the Go button and place the label in the middle, with the text +. You can add a bunch of constraints to each item. You can see all the constraints added in Figure 12.8, as well as the layout on the right. You can add these constraints manually by Control+dragging from each item and choosing the constraint. If you resize anything after a constraint is applied, you must update the constraint. As soon as you resize, you see lines turn orange, which means you messed up the constraints, tsk tsk. If this is the case, you should see the icons in Figure 12.9 at the bottom of your storyboard. You can click the triangle (see Figure 12.8) and click Update Constraints. The other option is to click Update Frames to resize your item to match the constraints. When adding constraints, you can also highlight an item and click the second icon on the left (the square with two lines), and you can add individual constraints that way as well.

Image

Figure 12.8 Constraint buttons in the Storyboard.

Image

Figure 12.9 Constraint editing options; may also be hieroglyphics from the year 2000 B.C.

When you run the app and rotate the iPhone or iPad, these items should stay centered. You can add references to these text fields in your code. Control+drag from the first text field to the code. For Connection, choose Outlet. For Name, enter addFirst. Then you can click Connect. Do the same thing for the second text field, except name it addSecond. After you hook those up, you can change the contents of your button-click listener, like this:

IBAction func buttonClicked(sender: AnyObject) {
println("Text1 \(addFirst.text) Text2 \(addSecond.text)")
}

Now you can get text out of the two text inputs. You can run this and give it a try.

Now you have a single view with input for the user and a button that responds to the user’s input. Now you need to take the contents of those two inputs and add them together and display them on the next page. The first step is to add another view, so go into your component search area and type in UIView. You should see a view controller pop up in the search. Drag it onto your screen, to the right-hand side of the original view, as shown in Figure 12.10.

Image

Figure 12.10 Adding a second view.

You can add a custom class to control this view individually. In your project navigator, add a new Swift file. In the new file dialog on the left-hand side, under iOS, choose Source, select New Swift File, and click Next. Name this file DetailView.swift. You can now create a detailed view that inherits from UIViewController. Add the following code to your detail view:

import UIKit
class DetailView: UIViewController {
}

After you create a detail view that inherits from the UIViewController, you can now have this code control the new view that you just created. Open your storyboard again and select the view controller button for that second view you just created. In the sidebar toolbar, make sure that the Identity inspector is selected, and for the class in the custom class, select the drop-down, where you should now see the new detail view as an option (see Figure 12.11).

Image

Figure 12.11 Changing the class for the view in the storyboard.

Select the detail view, and if you still have the assistant editor button at the top selected, your detail view should now appear on the right-hand side of the split screen. You now have the detail view hooked up to your new view. Now you need to create a variable to store the answer to the addition in your first view. Add this variable to your detail view:

class DetailView: UIViewController {
var total = 0
}

Now in your storyboard, select the first view, which should set your right-hand split screen to open up the ViewController.swift file. You need to create a segue from the first view to the second view. Control+drag from the Go button to the second view. When the dialog pops up, choose a show (that is, push) segue. You should now have the symbol shown in Figure 12.12 between your two views.

Image

Figure 12.12 A push segue.

If you ran this now, it would work just fine because the segue is going to respond to the click of the button. However, you don’t have any details on the second view yet. You also need to set details on the second view before the transition happens. In order to catch a segue before it happens and set some details, you can use this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)

This function returns the actual segue that is about to happen. Before the segue happens, you can set the total on the new view. Add this code to your view controller:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var controller = segue.destinationViewController as DetailView
controller.total = addFirst.text.toInt()! + addSecond.text.toInt()!
}

Here you are grabbing an instance of the controller that you are about to transition toward. You are also setting the total on that controller. All you have to do is add the two values from the text inputs together as integers. You are not really doing any error checking here.

Now you need to set a label on the second view so that you can show the total after it’s set. Search for a label in the list of components on the bottom-right side. Add that label to the second view. Try to line it up so that it’s horizontally and vertically centered. After you add the label, Control+drag the label to the left and add a Center Vertically in Container constraint. Control+drag up from the label as well and add a Center Horizontally in Container constraint. Your two constraints should now be blue, which means you’re good to go.

You need to create a reference for this label in the detail view. To do this, Control+drag from the storyboard’s second view label to the detail view (see Figure 12.13). Try not to grab the constraints of the label at the same time. (It helps if you deselect the label first.)

Image

Figure 12.13 Adding an outlet for the label so you can change its text.

In the pop-up that appears, choose Outlet for Connection, name it answerLabel, and click Connect.

You need to add a function that gets called when the detail view is loaded. This is a perfect job for viewDidLoad. Add this code to your detail view:

override func viewDidLoad() {
answerLabel.text = String(total)
}

Here you are setting the label to be what the total will be. The other view controller in prepareForSegue will set the total.

If you run the app, it should work. The total will come up on the second view. This process of creating segues and intersecting them before they arrive, while setting variables, is a very common way of doing things. You don’t have to create segues programmatically; you let the storyboard do most of the work. For each view, you should create a new view controller file.

Loading a Table View

The small app you just created should give you an idea of how to structure much larger apps. If you give each view its own file, that separation of concerns will keep things loosely coupled. This is an MVC (model view controller) without the model. When you’re loading data for a table view, you must have a model because table views contain data.

We will now create another application from scratch. Create a new single-view project, with all the same settings as before. Choose File > New > Project, choose Single View Application, and give it a name. Create that project and save it. You should now have a blank single-view application with the first view set. Delete that first view by selecting the view controller button on that view and pressing the Delete key. In the search component area in the bottom-right corner, you should see a table view controller, which you can drag onto the screen. You no longer have a first view set, so you need to set the new table view controller as the first view. To do this, select the view controller button of the new table view that you’ve added, and in your sidebar toolbar area, make sure the Attributes inspector (the fourth button from the left) is selected. In the View Controller section, you should see the check box Is Initial View Controller. Select this check box, and you should get an arrow in front of your view.

A table view accepts data, so you should create a model for all that data. Pretend that your table is going to store a bunch of cars, and you want to create a structure that will hold one individual car. In your project, create a new file called Car.swift, with the following contents:

struct Car {
var make:String
var year:String
var worth:Float
}

The view controller must inherit from UITableViewController in order to get all the benefits of the table that you are going to be controlling. This class needs to have a couple functions in order to work. You can add two of them right now:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
var cellIdentifier = "carCell"
}

Tables use cells to render information so you need to identify a cell that you are going to use in your table. So go back to the storyboard, and in the table view that you created, click once on the only cell in the table. Make sure in your sidebar toolbar that the Attributes inspector is selected. Change the Table View Cell Style setting from Custom to Subtitle, and name the cell identifier carCell. Now you can go back to your tableView cellForRowAtIndexPath. You are telling the table view to use this carCell in the code. Now you can add some cars to a car array. You need to create a couple methods that the table needs, so rewrite your ViewController.swift like this:

override func viewDidLoad() {
super.viewDidLoad()
cars.append(Car(make: "Ford", year: "1954", worth: 2_000_000.0))
cars.append(Car(make: "Mercedes", year: "2000", worth: 58_000.0))
cars.append(Car(make: "Yugo", year: "1975", worth: 0.20))
println(cars.count)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be re-created.
}

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return cars.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
var cellIdentifier = "carCell"
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier
(cellIdentifier, forIndexPath:
indexPath) as UITableViewCell
cell.textLabel?.text = "\(cars[indexPath.row].year) -
\(cars[indexPath.row].make)"
cell.detailTextLabel?.text = "$\(cars[indexPath.row].worth)"
return cell
}

Notice that every time you create a table, you need specific methods, in order, for the table function. You need at least need the following three methods:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {: This method tells Swift how to render each cell for the table. It will be sent an index path, which is the current row that it needs to render. In this case, you tell it to use the carCell cell that you created in the storyboard. In this function, you create a new reusable UITableViewCell. You assign the text to the cell. You also assign some detail text because you are using the subtitle cell. In the end, you must return the cell in order for the function to work properly.

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {: This method tells Swift how many rows will be in the table. You use the count of cars in the array. It’s good to use a variable here because it may change later. If it does, you won’t have to change this function, or write if statements; it will always work because the array count will fluctuate, and this method will be called whenever the table needs to be reloaded.

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {: This returns the number of sections for the table. Sections are the alphabetical sections you see in your contact list. When you aren’t using them (you’re not now), you can just have this function return 1.

With these three methods in place, your code is almost ready to run. You need to make one change, though. Because you deleted your original view and loaded a new view, you need to tell this new view to use the ViewController.swift file. You did this in the previous example for the detail view. Do you remember how to do it? Open the storyboard and select the view controller by clicking the view controller button on the top bar area of the view. In the sidebar toolbar area, make sure you select the Identity inspector, which is the third button from the left. For the class, choose ViewController from the drop-down shown in Figure 12.14.

Image

Figure 12.14 Assigning the view a class.

Now if you run the code, you should have a table with three items in it, as shown in Figure 12.15.

Image

Figure 12.15 The table with three items in it.

To format the money amounts so they look correct, open the view controller code, and rewrite the line that sets the detailed text as follows:

cell.detailTextLabel?.text = String(format:"$ %.2f",cars[indexPath.row].worth)

Here you are using a string formatter to make sure there are always two decimal places when formatting money.

Loading Data from a URL

To load data into a table, you could grab the data from a URL. The following is a small class that you could use to load the JSON data. Create a new file, name it Get.swift, and enter the following in it:

import Foundation

class Get {
class func JSON(url:String,callback:(NSDictionary)->()) {
requestJSON(url,callback)
}
class func requestJSON(url:String,callback:(NSDictionary)->()) {
var nsURL = NSURL(string: url)
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL) {
(data,response,error) in
var error:NSError?
var response = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers, error:
&error) as NSDictionary
callback(response)
}
task.resume()
}
}

How does this class work? You created two type methods, which can be called as follows:

Get.JSON("http://someurl.com") {
(response) in
// got data back, let's do something.
}

You call the Get.JSON() with a URL, and ending with a trailing closure. Once the data gets inside that closure, you have the response available to you as an NSDictionary.

You could write this in your viewDidLoad method. If you wanted to then reload the table with your new data, you could do something like this:

Get.JSON("http://someurlwithjsondata.com") {
(response) in
for car in response["cars"]!{
car.append(car)
}
dispatch_async(dispatch_get_main_queue()) {
println("reloading")
self.tableView.reloadData()
}
}

You must call dispatch_async because you must make any changes to the UI on the main thread. By using dispatch_async, you can put things on the main thread by specifying the main thread with dispatch_get_main_queue(). You can use the reload table function of the table view to reload the table, which will call all the necessary functions once again.

Summary

Many libraries are available that will load your data from a URL. You can easily use one of those libraries along with your knowledge of how to write Swift.

All the user interface controls in Xcode using Swift have specific ways of doing things. In this chapter you took a look at a couple of these, and there are many more different controls available. After you know the way that Apple wants you to write your code, it becomes easier to customize controls. When you are able to build a basic app using the methods described in this chapter, you’ll find it easy to take someone else’s idea and turn it into an app without much issue. Everyone’s going to be coming to you now with their great app ideas. And you will be ready to create those apps.