What You’ll Learn This Hour:
How to use segmented controls (a.k.a. button bars)
Ways of inputting Boolean values via switches
How to include web content within your application
The use of scrolling views to overcome screen limitations
After the last few lessons, you now have a good understanding of the basic iOS interface elements, but we’ve only just scratched the surface. Additional user input features are available to help a user quickly choose between several predefined options. After all, there’s no point in typing when a touch is enough. This hour’s lesson picks up where the last left off, providing you with hands-on experience with a new set of user input options that go beyond fields, buttons, and sliders.
In addition, we look at two new views you can use to present data to the user: web and scrolling views. These features make it possible to create applications that can extend beyond the hardware boundaries of your device’s screen and include content from remote web servers.
When I set out to write this book, I originally dedicated a couple of hours to the iOS interface widgets (fields, buttons, and so on). After we got started, however, it became apparent that for learning to develop on iOS, the interface was not something to gloss over. The interface options are what makes the device so enjoyable to use and what gives you, the developer, a truly rich canvas to work with. You’ll still need to come up with ideas for what your application will do, but the interface can be the deciding factor in whether your vision “clicks” with its intended audience.
In the past two hours, you learned about fields, sliders, steppers, labels, and images as input and output options. In this lesson, you explore two new input options for handling discrete values, along with two new view types that extend the information you can display to web pages and beyond.
In most traditional desktop applications, the choice between something being active or inactive is made by checking or unchecking a check box or by choosing between radio buttons. In iOS, Apple has chosen to abandon these options in favor of switches and segmented controls. Switches (UISwitch
) present a simple on/off user interface (UI) element that resembles a traditional physical toggle switch, as shown in Figure 9.1. Switches have few configurable options and should be used for handling Boolean values.
Note
Check boxes and radio buttons, although not part of the iOS UI Library, can be created with the UIButton
class using the button states and custom button images. Apple provides the flexibility to customize to your heart’s content—but sticking with controls your users will find familiar will often provide superior usability.
To work with the switch, we’ll make use of its Value Changed event to detect a toggle of the switch and then read its current value via the on
variable property.
The value returned when checking a switch is a Boolean, meaning that we can compare it to true
or false
to determine its state, or evaluate the result directly in a conditional statement.
For example, to check whether a switch mySwitch
is turned on, we can use code similar to this:
if (mySwitch.on) { <switch is on> } else { <switch is off> }
When user input needs to extend beyond just a Boolean value, you can use a segmented control (UISegmentedControl
). Segmented controls present a linear line of buttons (sometimes referred to as a button bar), within which a single button can be active within the bar, as shown in Figure 9.2.
Segmented controls, when used according to Apple guidelines, result in a change in what the user sees onscreen. They are often used to choose between categories of information or to switch between the display of application screens, such as configuration and results screens. For just choosing from a list of values where no immediate visual change takes place, the Picker object should be used instead. We look at this feature in Hour 12, “Making Choices with Toolbars and Pickers.”
Note
Apple recommends using segmented controls to update the information visible in a view. If the change, however, means altering everything onscreen, you are probably better off switching between multiple independent views using a toolbar or tab bar. We start looking at the multiview approach in Hour 11, “Implementing Multiple Scenes and Popovers.”
Handling interactions with a segmented control is very similar to handling them with the toggle switch. We’ll watch for the Value Changed event and determine the currently selected button through the selectedSegmentIndex
, which returns the number of the button chosen (starting with 0, from left to right).
We can combine the index with the object’s instance method titleForSegmentAtIndex
to work directly with the titles assigned to each segment. To retrieve the name of the currently selected button in a segmented control called mySegment
, we could use the following code fragment:
mySegment.titleForSegmentAtIndex(mySegment.selectedSegmentIndex)
We use this technique later in the lesson.
In the previous applications that you’ve built, you’ve used the typical iOS view: an instance of UIView
to hold your controls, content, and images. This is the view you will use most often in your apps, but it isn’t the only view supported in iOS. A web view, or UIWebView
, provides advanced features that open up a whole new range of possibilities in your apps.
Tip
In Hour 7, “Working with Text, Keyboards, and Buttons,” you used another view type, UITextView
, which provides basic text input and output, and which straddles the line between an input mechanism and what we’ll typically refer to as a view.
Think of a web view as a borderless Safari window that you can add to your applications and control programmatically. You can present HTML, load web pages, and offer pinching and zooming gestures, all “for free,” using this class.
You can also use web views to display a wide range of files, without needing to know anything about the file formats:
HTML, images, and CSS
Word documents (.doc/.docx)
Excel spreadsheets (.xls/.xlsx)
Keynote presentations (.key)
Numbers spreadsheets (.numbers)
Pages documents (.pages)
PDF files (.pdf)
PowerPoint presentations (.ppt/.pptx)
You can add these files as resources to your project and display them within a web view, access them on remote servers, or read them from an iDevice’s file storage (which you learn about in Hour 15, “Reading and Writing Application Data”).
Web views implement a method called loadRequest
that you can use to load an arbitrary URL; unfortunately, however, you can’t just pass it a string and expect it to work.
To load content into a web view, you’ll often use NSURL
and NSURLRequest
. These two classes enable you to manipulate URLs and prepare them to be used as a request for a remote resource. You first create an instance of an NSURL
object, most often from a string using the NSURL
convenience initialization method. For example, to create an NSURL
that stores the address for Apple’s website, you could use the following:
var appleURL: NSURL
appleURL=NSURL(string:"http://www.apple.com/")!
Notice the exclamation mark at the end of that line? The !
indicates that we’re “unwrapping” (accessing) the value that precedes it (that is, the object returned by the NSURL
initialization method). Why is this necessary? If you use the Xcode documentation for NSURL
, you’ll see that it may return nil
. In other words, it may return the NSURL
object we want, or, if something is wrong with the string, it may return nothing at all! Xcode wants us (very much) to understand and acknowledge this, so it forces us to explicitly “unwrap” the value by adding the !
. If you fail to add the exclamation mark, you’ll get an error, and Xcode will offer to add it for you.
Once the NSURL
object is created, you need to create an NSURLRequest
object that can be passed to a web view and loaded. To return an NSURLRequest
from an NSURL
object, we can use the NSURLRequest
convenience initialization method that, given an NSURL
, returns the corresponding request object:
NSURLRequest(URL: appleURL)
Finally, this value is passed to the loadRequest
method of the web view, which then takes over and handles loading the process. Putting all the pieces together, loading Apple’s website into a preexisting web view called appleView
looks like this:
var appleURL: NSURL
appleURL=NSURL(string:"http://www.apple.com/")!
appleView.loadRequest(NSURLRequest(URL: appleURL))
We implement web views in this hour’s first project, so you’ll soon have a chance to put this to use.
Tip
Another way that you get content into your application is by loading HTML directly into a web view. For example, if you generate HTML content in a string called myHTML
, you can use the loadHTMLString:baseURL
method of a web view to load the HTML content and display it. Assuming a web view called htmlView
, you might write this as follows:
htmlView.loadHTMLString(myHTML, baseURL: nil)
You’ve certainly used applications that display more information than what fits on a single screen; in these cases, what happens? Chances are, the application allows you to scroll to access additional content. Often, this is managed through a scrolling view, or UIScrollView
. Scrolling views, as their name suggests, provide scrolling features and can display more than a single screen’s worth of information.
Unfortunately, Apple has gone about halfway toward making scrolling views something that you can add to your projects using the Interface Builder (IB) tools. You can add the view, but until you add a line of code to your application, it won’t scroll. We close out this hour’s lesson with a quick example (a single line of code) that enables UIScrollView
instances that you add in IB to scroll your content.
As you’ve probably noticed by now, we prefer to work on examples that do something. It’s one thing to show a few lines of code in a chapter and say “this will do <blah>,” but it’s another to take a collection of features and combine them in a way that results in a working application. In some cases, the former approach is unavoidable, but this isn’t one of them. Our first hands-on example makes use of web views, a segmented control, and a toggle switch.
In this project, we create an application that displays flower photographs and flower information from the website FloraPhotographs.com. The application enables a user to touch a flower color within a segmented control (UISegmentedControl
), resulting in a flower of that color being fetched and displayed from the FloraPhotographs site in a web view (UIWebView
). The user can then use a toggle switch (UISwitch
) to show and hide a second web view that contains details about the flower being displayed. Finally, a standard button (UIButton
) enables the user to fetch another flower photo of the currently selected color from the site. The result should look very much like Figure 9.3.
This project will, once again, use the Single View Application template we’re starting to love. If it isn’t already running, launch Xcode, and then create a new project using the same settings as in the previous hours. Call this project FlowerWeb.
You should now be accustomed to what happens next. Xcode sets up the project and creates the default view in Main.storyboard and a view controller class named ViewController
. We’ll start as we always do: planning the variables, outlets, and actions we need in the view controller.
To create the web-based image viewer, we need three outlets and two actions. The segmented control will be connecting to variable property called colorChoice
because we’ll use it to choose which color is displayed. The web view that contains the flower will be connected to flowerView
, and the associated details web view to flowerDetailView
.
For the actions, the application must do two things: get and display a flower image, which we’ll define as the action method getFlower
; and toggle the flower details on and off, something we’ll handle with a toggleFlowerDetail
action method.
By now, this process should seem a bit familiar. We’ve defined the outlets and actions, so it’s time to build the UI. Prepare your Xcode workspace for developing the UI: Choose the Main.storyboard file to open the IB editor, and then close the project navigator, if necessary, to make room on your display.
As with the previous projects, I recommend selecting the view controller for your scene and then using the Attributes Inspector to set the simulated screen size to something familiar, like the 4.7-inch iPhone. Once your workspace is configured, we can get down to the design.
We begin by adding the segmented control.
To add a segmented control to the user interface, open the Object Library (View, Utilities, Object Library), find the segmented control (UISegmentedControl
) object, and drag it into the view. Position the control near the top of the view in the center. Because this control is ultimately used to choose colors, click and drag a label (UILabel
) into the view, as well, position it above the segmented control, and change it to read Choose a Flower Color:. Your view should now resemble Figure 9.4.
By default, the segmented control has two segments, titled First and Second. You can double-click these titles and edit them directly in the view, but that doesn’t quite get us what we need.
For this project, we need a control that has four segments, each labeled with a color: Red, Blue, Yellow, and Green. These are the colors that we can request from the FloraPhotographs website for displaying. Obviously, we need to add a few more segments to the control before all the choices can be represented.
The number of segments displayed in the segmented control is configurable in the Attributes Inspector for the object. Select the control that you’ve added to the view, and then press Option-Command-4 to open the Attributes Inspector, shown in Figure 9.5.
Using the Segments field, increase the number from 2 to 4. You should immediately see the new segments displayed. Notice that directly below where you set the number of segments in the inspector is a drop-down with entries for each segment you’ve added. You can choose a segment in this drop-down and then specify its title in the Title field. You can even add image resources and have them displayed within each segment.
Note
Note that the first segment is segment 0, the next is segment 1, and so on. It’s important to keep this in mind when you’re checking to see which segment is selected. The first segment is not segment 1, as you might assume.
Update the four segments in the control so that the colors Red, Blue, Yellow, and Green are represented. The segmented control should now have titles for all the colors and a corresponding label to help the user understand its purpose.
Note
iPad developers who want to take advantage of their extended screen space might try adding a few more segments to the segmented control. The colors violet and magenta can be added and will be automatically recognized by the FloraPhotographs site.
Chances are, the control you’ve set up doesn’t quite look right in the view. To size the control to aesthetically pleasing dimensions, use the selection handles on the sides of the control to stretch and shrink it appropriately. You can even optimize the size of individual segments by setting them to a fixed width using the Segmented Control Size options in the Size Inspector (Option-Command-5), as shown in Figure 9.6, or set the Auto-Size mode so that the segments are scaled according to the size of their content.
The next UI element we’ll add is the switch (UISwitch
). In our application, the switch has one role: to toggle a web view that displays details about the flower (flowerDetailView
) on and off. Add the switch to the view by dragging the switch object from the Object Library into the view. Position it along the right side of the screen, just under the segmented control.
As with the segmented control, providing some basic user instruction through an onscreen label can be helpful. Drag a label (UILabel
) into the view and position it to the left of the switch. Change the text to read Show Photo Details:. Your view should now resemble Figure 9.7, but your switch will likely show up as on.
I know you’re getting used to many of the different configuration options for the controls we use, but in this case, the switch has only a few options: whether the default state is on or off and what custom tints (if any) should be applied in the on state and to the “thumb” (the little round thing) of the switch.
The switch that you added to the view is set to on. We want to change it so that it is off by default. To change the default state, select the object and open the Attributes Inspector (Option-Command-4). Using the State pop-up menu (see Figure 9.7), change the default state to off.
The application that we’re building relies on two different web views. One displays the flower image itself; the other view (which can be toggled on and off) shows details about the image. The details view will be overlaid on top of the image itself, so let’s start by adding the main view, flowerView
.
To add a web view (UIWebView
) to your application, locate it in the Object Library and drag it into your view. The web view will display a resizable rectangle that you can drag and position anywhere you want. Because this is the view that the flower image is shown in, position it to fall about halfway down the screen, and then resize it so that it is the same width as the device screen and so that it covers the lower portion of the view entirely.
Repeat this to add a second web view for the flower details (flowerDetailView
). This time, size the view so that it is about half an inch high and locate it at the very bottom of the screen, over top of the flower view, as shown in Figure 9.8. Remember that you can drag items in the document outline to change their ordering. The closer an element is to the top of the list, the further “back” it is.
Web views, surprisingly, have few attributes that you can configure, but what is available can be very important. To access the web view attributes, select one of the views you added, and then press Open-Command-4 to open the Attributes Inspector (see Figure 9.9).
You can select from three types of settings: Scaling, Detection (Phone Numbers, Addresses, Events, Links), and a generic Options. If you select Scales Page to Fit under Scaling, large pages are scaled to fit in the size of the area you’ve defined. If you use Detection options, the iOS data detectors go to work and underline items that it has decided are phone numbers, addresses, dates, or additional web links. The Options settings apply to media playback—whether you can use AirPlay, how media plays in your web view (inline or not)—as well as to how web pages render (most do it incrementally) and whether pages are allowed to display a keyboard automatically.
For the main flower view, we absolutely want the images to be scaled to fit within the view. Select the web view, and then use the Attributes Inspector to choose the Scales Page to Fit option.
For the second view, we do not want scaling to be set, so select the web view where the application will be showing the flower details and use the Attributes Inspector to ensure that no scaling takes place. You might also want to change the view attributes for the detail view to have an alpha value of around 0.65. This creates a nice translucency effect when the details are displayed on top of the photograph.
Caution: Understand the Effects of Scaling
Scaling doesn’t necessarily do what you might expect for “small” web pages. If you display a page with only the text Hello World
on it in a scaled web view, you might expect the text to be shown to fill the web view. Instead, the text will be tiny. The web view assumes that the text is part of a larger page and scales it down rather than making it appear bigger.
If you happen to have control of the web page itself, you can add a "viewport"
meta tag to tell Safari how wide (in pixels) the full page is, as follows:
<meta name="viewport" content="width=320"/>
With the tough stuff out of the way, we just have one more finishing touch to put on the interface, and then we’re ready to code.
The only functional piece currently missing from our interface is a button (UIButton
) that we can use to manually trigger the getFlower
method anytime we want. Without the button, we have to switch between colors using the segmented control if we want to see a new flower image. This button does nothing more than trigger an action (getFlower
), something you’ve done repeatedly in the past few hours, so this should be quite easy for you by now.
Drag a button into the view, positioning it in the center of the screen above the web views. Edit the button title to read Get New Photo. We’re done. You know what that means: time to wire the interface to the code.
Tip
Although your interface may be functionally complete, you might want to select the view itself and set a background color. Keep your interfaces clean and friendly.
We have quite a few interface elements to connect for this project. Our segmented control, switch, button, and web views all need the proper connections to the view controller. Here’s what we’ll use.
Starting with the outlets:
Segmented control for choosing colors (UISegmentedControl): colorChoice
Main flower web view (UIWebView): flowerView
Flower detail web view (UIWebView): flowerDetailView
And then the actions:
Fetch a new flower using the Get New Flower button: getFlower
Turn the flower detail view on and off with the UISwitch: toggleFlowerDetail
Okay, the same old story: Prepare your workspace by making sure that the Main.storyboard file is selected and then opening the assistant editor. Hide the project navigator and document outline if you need space.
I assume you’re getting pretty familiar with this process, so we’ll move quickly through the connections here and in later hours. After all, it’s just click, drag, and connect.
Begin by Control-dragging from the segmented color button control to the line following the class
line in the ViewController.swift file. When prompted, configure the connection as an outlet and its name as colorChoice
, leaving the other values set to the defaults. This gives us an easy way to get the currently selected color in our code.
Continue building the rest of the outlets, connecting the main (large) web view to the outlet flowerView
by Control-dragging to just below the first @IBOutlet
line in ViewController.swift. Finish the outlets by connecting the second web view to flowerDetailView
with the same approach, as shown in Figure 9.10.
Our UI triggers two action methods. The switch hides and shows details about the flower through the method toggleFlowerDetail
, and the standard button loads a new image for us with getFlower
. Straightforward, right? It is, but sometimes you need to think beyond the obvious actions users can take and consider what they will expect to happen when they use the interface.
In this application, users are presented with a simple interface. They should immediately recognize that they can choose a color and push a button to get a flower of that color. But shouldn’t the application be smart enough to load a new flower as soon as the user switches the color? Why should the user have to switch the color and then press another button? By connecting the UISegmentedControl
’s Value Changed event to the same getFlower
method we trigger from the button, we gain this functionality without writing a single additional line of code.
Start by connecting the switch (UISwitch
) to a new action named toggleFlowerDetail
by Control-dragging to just below the last @IBOutlet
directive in the ViewController.swift file. Make sure that the action is triggered from the event Value Changed, as shown in Figure 9.11.
Next, Control-drag from the button (UIButton
) to a line under the @IBAction
you just defined. When prompted, configure a new action, getFlower
, that is triggered from the Touch Up Inside event. Finally, we need to target this new getFlower
action from the Value Changed event on the segmented control (UISegmentedControl
) so that the user can load a new flower just by touching a color.
Switch to the standard editor and make sure that the document outline is visible (Editor, Show Document Outline). Select the segmented control, and open the Connections Inspector by pressing Option-Command-6 (View, Utilities, Connections Inspector). Drag from the circle beside Value Changed to the View Controller line in the document outline, as demonstrated in Figure 9.12. Release your mouse button and choose getFlower
when prompted.
Note
Okay, I admit it. In this example, we took a roundabout way of connecting the segmented control to the getFlower
method. We could have also Control-dragged from the segmented control onto the getFlower IBAction
line in the assistant editor and instantly had our connection.
The problem with the “easy” approach, however, is that you aren’t given the opportunity of choosing which event triggers the action. It just so happens that in this case, Xcode would choose Value Changed
for you.
The interface and its connections are finished. The block of code at the top of your ViewController.swift file should look much like this:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var colorChoice: UISegmentedControl!
@IBOutlet weak var flowerView: UIWebView!
@IBOutlet weak var flowerDetailView: UIWebView!
@IBAction func getFlower(sender: AnyObject?) {
}
@IBAction func toggleFlowerDetail(sender: AnyObject) {
}
Our view controller needs to implement two pieces of functionality via two action methods. The first, toggleFlowerDetail
, shows and hides the flowerDetailView
web view, depending on whether the switch has been flipped on (show) or off (hide). The second method, getFlower
, loads a flower image into the flowerView
web view and details on that photograph into the flowerDetailView
web view. We start with the easier of the two, toggleFlowerDetail
.
A useful feature of any object that inherits from UIView
is that you can easily hide (or show) it within your iOS application interfaces. Because almost everything you see onscreen inherits from this class, this means that you can hide and show labels, buttons, fields, images, and yes, other views. To hide an object, you just set its Boolean variable property hidden
to true
. So, to hide the flowerDetailView
, we write the following:
flowerDetailView.hidden = true
To show it again, we just reverse the process, setting the hidden
property to false
:
flowerDetailView.hidden = false
To implement the logic for the toggleFlowerDetail
method, we need to figure out what value the switch is currently set to. As mentioned earlier in the lesson, we can check the state of a toggle switch through the on
variable property: true
if the switch is set to on, false
if it is off.
Because we don’t have an outlet specifically set aside for the switch, we’ll use the sender
variable to access it in our method. When the toggleFlowerDetail
action method is called, this variable is set to reference the object that invoked the action (in other words, the switch). Unfortunately, the sender
is defined as being of the type AnyObject
, so we’ll get an error if we try to access the on
variable property because Xcode doesn’t know that it’s a switch. We could just redefine the method so that the incoming parameter is a UISwitch
(rather than AnyObject
), but why take the easy way out? Instead, we can type cast sender
to be recognized as a UISwitch
by using the syntax <variable> as <object>
.
In other words, to access sender
as an object of type UISwitch
, we use the format sender as UISwitch
. To access the on
variable property, wrap the type cast object in parentheses like this:
(sender as UISwitch).on
So, to check to see whether the switch is on, we can write the following:
if (sender as UISwitch).on { <switch is on> } else { <switch is off> }
Now, here’s where we can get clever. (You are feeling clever, right?) We want to hide and show the flowerDetailView
using a Boolean value and we get a Boolean value from (sender
as UISwitch).on. This maps to two conditions:
When (sender
as UISwitch.
on is true
, the view should not be hidden (flowerDetailView.hidden = false).
When (sender
as UISwitch).on is false
, the view should be hidden (flowerDetailView.
hidden =
true).
In other words, the state of the switch is the exact opposite of what we need to assign to the hidden
variable property of the view. In Swift, to get the opposite of a Boolean value, we just put an exclamation mark in front (!
). So, all we need to do to hide or show flowerDetailView
is to set the hidden
variable property to !(sender
as UISwitch).
on. That’s it. A single line of code.
Implement toggleFlowerDetail
in the FlowerWeb
method stub that Xcode provided for you. The full method should look a lot like Listing 9.1.
@IBAction func toggleFlowerDetail(sender: AnyObject) {
flowerDetailView.hidden = !(sender as UISwitch).on
}
To fetch our flower images, we use a feature provided by the FloraPhotographs website specifically for this purpose. We follow four steps to interact with the website:
1. We get the chosen color from the segmented control.
2. We generate a random number called a “session ID” so that FloraPhotographs.com can track our request.
3. We request the URL http://www.floraphotographs.com/showrandomios.php?color=<color>&session=<sessionID>, where <color>
is the chosen color and <session ID>
is the random number. This URL returns a flower photo.
4. We request the URL http://www.floraphotographs.com/detailios.php?session=<sessionID>, where <session ID>
is the same random number. This URL returns the details for the previously requested flower photo.
Let’s go ahead and see what this looks like in code and then discuss details behind the implementation. Add the getFlower
implementation, as shown in Listing 9.2.
1:@IBAction func getFlower(sender: AnyObject?) {
2: var imageURL: NSURL
3: var detailURL: NSURL
4: var imageURLString: String
5: var detailURLString: String
6: var color: String
7: let sessionID: Int=random()%50000
8:
9: color=colorChoice.titleForSegmentAtIndex(colorChoice.selectedSegmentIndex)!
10:
11: imageURLString =
12:"http://www.floraphotographs.com/showrandomios.php?color=(color)&session=(sessionID)"
13: detailURLString =
14: "http://www.floraphotographs.com/detailios.php?session=(sessionID)"
15:
16: imageURL=NSURL(string: imageURLString)!
17: detailURL=NSURL(string: detailURLString)!
18:
19: flowerView.loadRequest(NSURLRequest(URL: imageURL))
20: flowerDetailView.loadRequest(NSURLRequest(URL: detailURL))
21: }
This is the most complicated code that you’ve written so far, but let’s break it down into the individual pieces, so it’s not difficult to understand.
In line 1, I’ve added a ? after AnyObject
because I want to be able to call this method without providing a value for sender
. We’ll get back to the reason behind this a bit later.
Lines 2–6 declare the variables that we need to prepare our requests to the website. The first variables, imageURL
and detailURL
, are instances of NSURL
that contain the URLs that are loaded into the flowerView
and flowerDetailView
web views. To create the NSURL
objects, we need two strings, imageURLString
and detailURLString
, which we format with the special URLs that we presented earlier, including the color
and sessionID
values.
Line 7 declares a constant sessionID
as an integer and assigns it a random number between 0
and 49999
. I’ve used the let
(insteed of var
) keyword to denote that this is a constant rather than a variable—but a variable would work just as well.
In line 9, we retrieve the title of the selected segment in our instance of the segmented control: colorChoice
. To do this, we use the object’s instance method titleForSegmentAtIndex
along with the object’s selectedSegmentIndex
variable property. The result, colorChoice
.
titleForSegmentAtIndex(
colorChoice.
selectedSegmentIndex)!
is stored in the string color
and is ready to be used in the web request. Notice the exclamation mark at the end of that line? The !
indicates that we’re explicitly unwrapping the value returned by titleForSegmentAtIndex
. This is required because the definition of the method indicates it could return a string; or, if a title was never set, it could return nil
. Obviously we’ve set titles for everything, so this should be a nonissue for us. However, because titleForSegmentAtIndex
was written to return optional strings, we have to acknowledge this fact and explicitly tell Xcode we want to use (unwrap) the value by adding the !
to the end.
Lines 11–14 prepare imageURLString
and detailURLString
with the URLs that we will be requesting. The color and session ID are substituted into the string definitions using “string interpolation.” That is, we add in the variables we want substituted into the string (color
and sessionID
) by placing their names in parentheses and adding a backslash to the start: (color)
and (sessionID)
.
Lines 16–17 create the imageURL
and detailURL NSURL
objects using the NSURL
convenience initialization method and the two strings imageURLString
and detailURLString
. Like the selectedSegmentIndex
in line 9, we have to explicitly unwrap the value returned by NSURL
by adding an !
to the end, because NSURL
documentation states that it may return no value if the string you give it is bad. Because we know we’re giving it a good URL, we can add the !
to the end and move on.
Lines 19–20 use the loadRequest
method of the flowerView
and flowerDetailView
web views to load the NSURL
s imageURL
and detailURL
, respectively. When these lines are executed, the display updates the contents of the two views.
Note
Remember that UIWebView
’s loadRequest
method doesn’t handle NSURL
objects directly; it expects an NSURLRequest
object instead. To work around this, we create and return NSURLRequest
objects using the NSURLRequest
convenient initialization method.
Now that the getFlower
method is implemented, you can run the application and everything should work—except that when the application starts, the two web views are empty and the detail view is visible, even though the toggle switch is set to off.
To fix this, we can start loading an image as soon as the app is up and running and set flowerDetailView.hidden
to true
. To do this, update the view controller’s viewDidLoad
method as follows in Listing 9.3.
override func viewDidLoad() {
super.viewDidLoad()
flowerDetailView.hidden=true
getFlower(nil)
}
As expected, flowerDetailView.hidden=true
hides the detail view. Using getFlower
(nil
), we can call the getFlower
method from within our instance of the view control and start the process of loading a flower in the web view. The method getFlower
expects a parameter, so we pass it nil
, just as we did in the last hour’s lesson. This is also the reason why we added the ?
to the getFlower
method definition, making the sender
’s value optional. If it weren’t optional, we would need to pass it some kind of object; otherwise, the application wouldn’t compile. Because it is optional, sending nil
works just fine. Keep in mind that we don’t actually use sender
for anything in the getFlower
method, so we don’t really care what value it has.
Test out the final version of the FlowerWeb application by clicking Run in Xcode.
Notice that you can zoom in and out of the web view and use your fingers to scroll around. These are all features that you get without any implementation cost when using the UIWebView
class.
Congratulations. Another app under your belt.
After working through the projects in the past few hours, iPhone users might begin to notice something: We’re running out of space in our interfaces. Things are starting to get cluttered.
One possible solution, as you learned earlier in this hour, is to use the hidden
variable property of UI objects to hide and show them in your applications. Unfortunately, when you’re juggling a few dozen controls, this is pretty impractical. Another approach is to use multiple different views, something that you start learning about in Hour 11.
There is, however, a third way that we can fit more into a single view: by making it scroll. Using an instance of the UIScrollView
class, you can add controls and interface elements to a canvas that stretches beyond the physical boundaries of your device’s screen. Unfortunately, although Apple provides access to this object in the IB editor, making it work is a bit less obvious than one might hope.
Before closing out this hour, I want to show you how to start using simple scrolling views in a mini-project.
When I say simple, I mean it. This project consists of a scroll view (UIScrollView
) with content added in the IB editor that extends beyond the physical screen, as shown in Figure 9.13.
To enable scrolling in the view, we need to set a variable property called contentSize
, which describes how large the content is that needs to be scrolled. That’s it.
Begin by creating another Single View Application. Name the new project Scroller. For this example, we’re going to be adding the scroll view (UIScrollView
) as a subview to the existing view in Main.storyboard. This is a perfectly acceptable approach, but as you get more experienced with the tools, you might want to just replace the default view entirely.
We need to do just one thing programmatically in this project, and that’s set a variable property on the scrollView
object. To access the scroll view, we’ll connect it, via an outlet, to a new variable property, which we’ll call theScroller.
There isn’t much to this project, just a scrolling view and content. Because you already know how to find objects in the Object Library and add them to a view, this should be trivial. Start by opening the Main.storyboard file for the project and making sure the document outline is visible (Editor, Show Document Outline).
Next, do the same thing you’ve done in previous projects: Set a simulated size for the view controller by selecting the View Controller line in the document outline and then opening the Attributes Inspector (Option-Command-4). Set the Size drop-down to the iOS device screen size that you want (iPhone 4.7-inch for me).
Using the Object Library (View Utilities, Show Object Library), drag an instance of a scroll view (UIScrollView
) into your view. Position the view to just about fill the scene using the guides on the sides and bottom to line it up. Place a label above it that reads Scrolling View (just in case you forget what we’re building). Figure 9.14 shows what your scene should look like at this point.
Tip
The text view (UITextView
) you used in Hour 7 is a specialized instance of a scrolling view. The same scrolling attributes that you can set for the text view can be applied for the scroll view, so you might want to refer to that earlier hour for more configuration possibilities. Or just press Option-Command-4 to bring up the Attributes Inspector and explore.
Now that your scroll view is added to your design, you need to populate it with something. Objects are often placed in scroll views by writing code that calculates their position—but how can we do that in such a small space?
To properly lay out a scrolling interface, we change the view controller and its associated view so that they are as large as the scrolling content we want them to hold.
Click the View Controller
object in the document outline, and then open the Size Inspector (Option-Command-5). Use the Size drop-down menu to switch from Fixed to Freeform.
Make a note of the current value of the Height for the view controller, then set it to roughly double what it is usually—that is, 1000 points for the iPhone and 2000 points for the iPad, as shown in Figure 9.15.
As soon as you make the change, your scene should change to take up the expanded space, as shown (zoomed out) in Figure 9.16.
Now that your scene is much larger, adding objects to your scrolling view should be straightforward. First, make sure that the scrolling view expanded when you set the new size for the view controller. If it didn’t, use the selection handles to resize it to fill your new space, snapping it to the guides on the bottom and left/right sides of the main view.
Add several labels (or any other GUI object) to your scrolling view, spacing them evenly from the top to the bottom. For my sample project, I’m adding six labels: Label 1, Label 2, ... Label 6. Figure 9.17 shows my final layout.
As a finishing step for the interface, we need to get the view controller and its content back to a size we can deal with (that is, set a “normal” simulated size).
Select the main View Controller line within the scene and open the Attributes Inspector (not the Size Inspector). Set the Simulated Metrics Size back to the original device that you were using (again, iPhone 4.7-inch in my projects). After making the change, your scene should shrink back down to a manageable size, and several of the labels should be hidden. If your scrolling view does resize correctly when you change the size of the main view, use the selection handles to resize it (or use the Size Inspector).
Resize and reposition the scrolling view and the label so that they fit nicely in the display. Now you should have a view, with labels (several hidden), in a “normal”-sized scene, as shown in Figure 9.18.
Now that your interface is finished, its time to get to coding. Don’t worry, you’ll be done before you even know you’ve started!
Another way to get labels into a scrolling view is to lay them out in your main view however you’d like, select them, and then choose Editor, Embed In, Scroll View from the menu bar. This adds a scrolling view to your interface that contains all the selected objects.
This project needs only a single outlet and no actions. To create the outlet, switch to the assistant editor and disable the project navigator if you need to make a bit of extra room. Control-drag from the scroll view to the line following the class
line in the ViewController.swift file.
When prompted, create a new outlet called theScroller, as shown in Figure 9.19.
That finishes up our work in the IB editor; just a teensy bit of implementation remains. Switch back to the standard editor, enable the project navigator, and then select the ViewController.swift file to finish the implementation.
For fun, try to run the application as it stands. It compiles and launches, but it doesn’t scroll (at least not correctly). The reason for this is because iOS needs to know the horizontal and vertical sizes of the region it is going to scroll. Until the scrolling view knows that it can scroll, it doesn’t.
To add scrolling to our scroll view, we need to set the contentSize
attribute to a CGSize
value. CGSize
is just a simple C data structure that contains a height and a width, and we can easily make one using the CGSizeMake(<width>,<height>)
function.
For example, to tell our scroll view (theScroller
) that it can scroll up to 300 points horizontally and 1,000 points vertically, we could enter the following:
theScroller.contentSize=CGSizeMake(300.0,1000.0)
Guess what? That isn’t just what we could do, it’s what we will do. If you’re developing on the iPhone, edit the ViewController.swift file and update the method viewDidLoad
shown in Listing 9.4.
override func viewDidLoad() {
super.viewDidLoad()
theScroller.contentSize=CGSizeMake(300.0,1000.0)
}
If you are working on the iPad, you need to adjust the size to be a bit larger because the screen is larger. Provide the arguments 900.0
and 2000.0
to the CGSizeMake
function, instead of 300.0
and 1000.0
.
The single line we added to the method line sets the contentSize
and enables the scrolling, ta da!
Caution: Wrong Makes Right
Depending on how much attention to pay to developer forums, you’ll find that some who proselytize the notion that Auto Layout (a system you’ll learn about in Hour 16, “Building Responsive User Interfaces”) will automatically calculate the sizes of your scrolling views and will make them scroll automatically. Adding additional code is unnecessary.
Unfortunately, Auto Layout is still rapidly evolving, and many developers have spent sleepless nights trying to make it work for scrolling views. As of iOS 8, it’s still not perfect. If we have to add two or three lines of code to ensure that it works right, no matter what, that seems like the smart thing to do.
The moment of truth has arrived. Does the single line of code make magic? Choose Run from the Xcode toolbar, and then try scrolling around the view you created. Everything should work like a charm.
Tip
Okay, so it’s not technically a single line of code for the entire implementation, but it is a single line that makes the scrolling view work.
Yes, this was a quick-and-dirty project, but there seems to be a lack of information on getting started with UIScrollView
, and I thought it was important to run through a short tutorial. I hope this gives you new ideas about what you can do to create more feature-rich iOS interfaces.
As well as useful, the segmented control (UISegmentedControl
) and switch (UISwitch
) classes are pretty easy to get the hang of. The best place to focus your attention for additional exploration is on the feature set provided by the UIWebView
and UIScrollView
classes.
As described at the start of this hour, UIWebView
can handle a large variety of content beyond what might be inferred by the “web” portion of its name. By learning more about NSURL
, such as the initFileURLWithPath:isDirectory
method, you’ll be able to load files directly from your project resources. You can also take advantage of the web view’s built-in actions, such as goForward
and goBack
, to add navigation functionality without a single line of code. One might even use a collection of HTML files to create a self-contained website within an application. In short, web views extend the traditional interface of your applications by bringing in HTML markup, JavaScript, and CSS—creating a potent combination.
The UIScrollView
class, in contrast, gives us an important capability that is widely used in iOS applications: touch scrolling. We briefly demonstrated this at the end of the hour, but you can enable additional features, such as pinching and zooming, by implementing the UIScrollViewDelegate
protocol. We take our first look at building a class that conforms to a protocol in the next hour, so keep this in mind as you get more comfortable with the concepts.
In this hour, you learned how to use two controls that enable applications to respond to user input beyond just a simple button press or a text field. The switch and segmented control, while limited in the options they can present, give a user a touch-friendly way of making decisions within your applications.
You also explored how to use web views to bring web content directly into your projects and how to tweak it so that it integrates into the overall iOS user experience. This powerful class will quickly become one of your most trusted tools for displaying content.
Because we’ve reached a point in our development where things are starting to get a bit cramped, we closed out the hour with a quick introduction to the scroll view. You learned how that, despite appearances, you can easily add scroll views to apps.
Q. Why did objects jump around when I resized the freeform view in IB?
A. If your labels or scrolling view moved around a bit, you’re seeing the results of the Auto Layout constraints system. You’ll learn more about this important tool in Hour 16, but for now, don’t worry too much if things don’t look exactly the way you want in your layouts.
Q. You mentioned the UIWebView includes actions. What does that mean, and how do I use them?
A. This means that the object you drag into your view is already capable of responding to actions (such as navigation actions) on its own—no code required. To use these, you connect from the UI event that should trigger the action to your instance of the web view and then choose the appropriate action from the pop-up window that appears.
1. What common iOS view feature can you add to a project through the Visual Effect View?
a. Blurring
b. Flashing
c. Transparency
d. Voiceover functionality
2. Which operator, when placed at the start of a Boolean value, results in the opposite of that value?
a. ?
b. *
c. &
d. !
3. What type of object does a web view expect as a parameter when loading a remote URL?
a. NSURL
b. NSURLRequest
c. NSRequestURL
d. LoadRequest
4. What variable property can be set to true
on a view to make it not visible?
a. invisible
b. noshow
c. nodisplay
d. hidden
5. If we wish to use a method and not supply a value to one of its parameters, that parameter must be set as which of the following?
a. Transient
b. Unwrapped
c. Empty
d. Optional
6. If an instance method returns an optional value, we can only use it after doing what?
a. Opening it
b. Unwrapping it
c. Displaying it
d. Querying it
7. Setting a view controller’s size to which of the following enables resizing?
a. Freeform
b. Unlimited
c. Draggable
d. Fixed
8. The selectedSegmentIndex
variable property of a segmented control assigns the first segment what value?
a. 0
b. 1
c. –1
d. nil
9. To typecast an object to another type, you use the syntax <myObject> ____ <cast object type>. What keyword is missing in the middle?
a. with
b. from
c. as
d. using
10. A method that quickly creates and returns an object is called what?
a. An initialization method
b. A quick start method
c. A convenience initialization method
d. A swift initialization method
1. A. Visual effects, such as the frosted glass/blurring effect in iOS, are easily added via the UIVisualEffectView
.
2. D. To get the opposite of a Boolean value, simply prefix it with the !
character.
3. B. The NSURLRequest
object is needed prior to making a remote web request.
4. D. Set the hidden
variable property to true
to quickly hide most visual elements in iOS.
5. D. Method parameters must be defined as Optional if you want to invoke them without a value.
6. B. Methods that return an optional value must be unwrapped before use.
7. A. The Freeform size option gives us the ability to resize a view control (and its contents) to any size we want.
8. A. The first segment in a segmented control is considered the 0
segment.
9. C. To typecast one object to another, use the keyword as
, ie: <myObject> as
<another Object type>.
10. C. Methods that quickly create and return a new object are known as convenience initialization methods.
1. Create your own “mini” web browser by combining a text field, buttons, and a segmented control with a web view. Use the text field for URL entry, buttons for navigation, and hard-code some shortcuts for your favorite sites into the segmented control. To make the best use of space, you may want to overlay the controls on the web view and then add a switch that hides or shows the controls when toggled.
2. Practice laying out a user interface within a scrollable view. Use graph paper to sketch the view and determine coordinates before laying it out in Interface Builder.
3. Change the FlowerWeb application to take on a more “iOS 8” appearance. Extend the main UIWebView
to cover the entire screen, but blur the area behind the main controls.