Got Views - Learn iOS 8 App Development, Second Edition (2014)

Learn iOS 8 App Development, Second Edition (2014)

Chapter 10. Got Views?

You now have a lot of experience adding view objects to your design, arranging them, connecting them to outlets and actions, and customizing them. You’ve created iOS apps using buttons, labels, image views, a few text fields, and the odd toolbar. While you probably haven’t been yearning for other kinds of view objects, there are more available. The Cocoa Touch framework provides all kinds of switches, toggles, sliders, specialty buttons, pickers, indicators, doo-dads, and gizmos you can use to build your apps. And if that isn’t enough, many of those objects can be customized in ways you haven’t explored yet. In this chapter, you’ll learn about the following:

· Downloading sample projects

· Button views

· Switches, sliders, and steppers

· Indicators

· Labels, text fields, and text views

· Pickers

· Grouped table views

· Scroll views

· Search controls

· Alerts and action sheets

Anyone who’s spent time building Lego figures, Erector Set constructions, or experimental aircraft will know one thing: your ability to imagine what’s possible is directly linked to your knowledge about what parts you have to work with. To that end, I invite you on a guided tour of iOS view objects.

Learning by Example

Software development is a lot like cooking. It’s one thing to read recipes, talk about the process, and enjoy the results. It’s another thing to actually do it. One of the best ways to learn how to cook is to watch someone who knows what they’re doing and emulate them.

Apple provides many example projects—fully written, ready-to-run apps—that demonstrate the use of various technologies in iOS. All you have to do is download one, build it, run it, and then mine it for all of its secrets. These example projects are a great way to get started using, or at least understanding how to use, many of the frameworks and features in iOS.

Not only does Apple provide these example projects free of charge, it has made it ridiculously simple to download them. Xcode will search for, download, and open sample code projects with the click of a button. Start in Xcode’s documentation window (Help image Documentation and API Reference), as shown in Figure 10-1.

image

Figure 10-1. Searching for sample code

Search for the UICatalog project, as shown in Figure 10-1. Click it and the project’s documentation page appears. Near the top is an Open Project link. Click it. Xcode downloads the project’s archive file, un-archives it, and opens the project in a new workspace window, as shown in Figure 10-2. How easy was that?

image

Figure 10-2. UICatalog project

Note Sample projects are not part of the Xcode installation package and require an Internet connection to download.

You’ll find that the documentation for many classes contains links to sample projects, making it easy to download code that shows you those classes in action.

Tip While Apple’s so-called walled garden keeps most iOS app projects within the developer community (after all, you have to be a developer to build and run apps on your iOS device) that hasn’t stopped the open source community. Many open source iOS projects are available to developers (like you) and to brave individuals who have “jailbroken” their device. A quick Internet search will turn up open source apps, as well as frameworks and code libraries, you can use in your own projects.

The UICatalog project is extra-special. It’s an app that demonstrates every major view object supplied by iOS. So, not only is it a handy visual reference to the kinds of view objects iOS supplies, but you can see exactly how these objects are created and used in an app.

Run the UICatalog app in a simulator (or on your own device, if you like), as shown in Figure 10-3. As of this writing, UICatalog contains two projects: one app written in Objective-C and an identical app written in Swift. When you download them, Xcode will open both projects. It doesn’t matter which one you run. For exploring the code, you’ll be interested in the Swift version, so close the Objective-C project.

image

Figure 10-3. UICatalog app

The list consists of the core set of stand-alone UIKit view classes, in alphabetical order, followed by the specialty search and toolbar classes. Let’s start with everyone’s favorite control: the button.

Buttons

A button is a straightforward view object; it acts like a physical button. The UIButton class draws a button and observes touch events to determine how the user is interacting with it. It translates what the user is doing into action events, such as “user touched inside the button,” “user moved their finger outside the button,” “user moved back inside the button,” and “user released their finger while still inside the button.” That’s pretty much all it does.

I know what you’re thinking. Well, maybe I don’t. But I hope you’re thinking, “But a button does more than that! It sends action messages to another object, it remembers its state, it can be disabled, and it can have gesture recognizers attached to it. That’s a lot!”

It is a lot, but the UIButton class doesn’t do any of those things. UIButton is at the end of a chain of classes, each of which is responsible for one set of closely related behaviors. Software engineers say that each class performs a role. The role of a UIButton object is to act like a button. Its superclasses do all of that other stuff. So that you can get a clearer picture of what’s going on, I think it’s time for you to dissect a UIButton. This will help you understand not only how UIButton was built but how all control views are constructed.

Note No UIButton objects were harmed in the making of this book.

The Responder and View Classes

A UIButton is a subclass of UIControl, which is a subclass of UIView, which is a subclass of UIResponder, as shown in Figure 10-4. Each class adds one layer of functionality that, taken together, makes a button.

image

Figure 10-4. Anatomy of a button

The UIResponder class defines all the event-related functions of the objects, most notably the methods that handle touch events. You learned all about UIResponder in Chapter 4, and you created a custom UIView object that overrode the touch event handling methods with your own, so I won’t repeat any of that here.

The next layer that UIButton inherits is UIView. UIView is a big, complicated class. It has dozens of properties and more than 100 methods. It’s huge because it’s responsible for every aspect of how every visible object in the iOS universe gets displayed on a screen. It handles the geometry of the view, its coordinate systems, transformations (such as rotation, scaling, and skewing), animation, how the view gets repositioned when the screen size changes, and hit testing. It’s also responsible for drawing itself, drawing its subviews, deciding when those views need to be redrawn, and more. You’ll get very intimate with UIView in Chapter 11.

One seemingly unrelated property of UIView is its gestureRecognizers property. The UIView class doesn’t do anything with gesture recognizers directly. But a UIView defines a visible region of the display, and any visible region can have a gesture recognizer attached to it so that property exists in UIView.

HOW GESTURE RECOGNIZERS GET EVENTS

Gesture recognizers are fed events by the UIWindow object during touch event delivery. In Chapter 4, I explained that hit testing is used to determine the view that will receive the touch events. That description oversimplified the process somewhat.

Starting with iOS 3.2, the UIWindow first looks at the initial (hit test) view to see whether it has any gesture recognizer objects attached to it. If it does, the touch events are first sent to those gesture recognizer objects, instead of being delivered directly to the view object. If the gesture recognizers aren’t interested, then the event eventually makes its way to the view object.

If you need to, there are a variety of ways to alter this behavior, but it’s a tad complicated. For all of the details, read the “Gesture Recognizers” chapter of the Event Handling Guide for iOS that you’ll find in Xcode’s Documentation and API Reference window.

So, everything that’s visual about the button is defined in the UIView class. Now move on to the next layer, the UIControl class.

The Control Class

UIControl is the abstract class that defines the properties common to all control objects. This includes buttons, sliders, switches, steppers, and so on. A control object does the following:

· Sends action messages to target objects

· Can be enabled or disabled

· Can be selected

· Can be highlighted

· Establishes how content is aligned

The first item in the previous list is the most important. The UIControl class defines the mechanism for delivering action messages to recipients, typically controller objects. Every UIControl object maintains a table of events that trigger actions, the object that will receive that action, and the action message it will send. When you’re editing an Interface Builder file and you connect an event to an action method in another object, you’re adding one entry to that object’s event dispatch table.

Note Remember from Chapter 4 that an event can be associated with an action that is sent to the first responder. You do this by specifying the message to send and using nil as the target object for that message.

The other properties (enabled, selected, and highlighted) are general indicators of the control’s appearance and behavior. Subclasses of UIControl determine exactly what those properties mean, if anything.

The enabled property is the most consistent. A control object interacts with the user when enabled and ignores touch events when disabled (control.enabled = false). Most control classes indicate that they are disabled by dimming, or graying, their image to show the user the control is inert.

The highlighted property is used to indicate that the user is currently touching the control. Many controls “light up” when touched, and this property reflects that.

The selected property is for controls that can be turned on or off, such as the UISwitch class. Controls that don’t, such as buttons, ignore this property.

The UIControl class also introduces the concept of an alignment (vertical and horizontal) through the contentVerticalAlignment and contentHorizontalAlignment properties. Most control objects have some sort of title or image and use these properties to position that in the view.

Button Types

You’ve now worked your way back to the UIButton class. It’s this class that implements the button-specific behavior of a control+view+responder object. The UIButton class supplies a handful of predefined button looks, along with a plethora of customizations so you can make it look just about any way you want.

The most important property of a button is its type. A button can be one of the following types:

· System

· Custom

· Detail disclosure

· An “info” button (light or dark)

· An “add contact” button

All of these button styles are shown in the UICatalog app, except for the detail disclosure and custom types, as shown in Figure 10-5. The important thing to remember is that a button’s type is determined when it is created. Unlike all of its other properties, it cannot be changed afterward. An info button is an info button for life.

image

Figure 10-5. Buttons

The system button is the workhorse of iOS. It’s the standard, default button style used throughout the iOS interface. The premiere properties that adjust your button’s look are as follows:

· Tint color

· Title text (plain or attributed) and color

· Foreground image

· Background image or color

The tintColor property sets the highlight and accent color of the button. The standard color is blue.

The button’s title can be a simple string value (which you’ve used in your projects so far), or it can be attributed string. An attributed string is a string that includes text attributes: font, size, italics, bold, subscript offset, and so on. Creating attributed strings is a bit complicated but allows you to create buttons with whatever font and style the system is capable of. I describe attributed strings in Chapter 20.

You can also use an image instead of text for your button’s label by setting its image property. Similarly, the background can be set to an image or a solid color. You can also mix these in any combination you want: text title over an image background, image over a solid color background, image with no background (by setting the background color to UIColor’s clearColor object), and so on.

Images used for a button’s background can utilize an interesting feature that allows the graphic to resize its center portion independently of its edges. The cap insets (capInsets) property defines a margin around the edge of the image that is not scaled when the image is stretched to fit the size of the button. This property lets you design a single graphic image that will fill any button size, without distorting its edges.

At the other extreme is the custom type. This is a blank canvas, and iOS adds nothing to the look or behavior of the button. You can still use the title and image properties, but most of the standard button behavior (such as highlighting) is left up to you.

The detail disclosure type is the right-facing arrow button that normally appears only in the cells of a table view. The add contact button displays a plus (+) symbol, and the two info buttons present a localized symbol indicating more information is available. These four types have predefined appearances and sizes, and there’s almost nothing about them that you can customize.

Note There’s a rounded rectangle button type that is obsolete and should be ignored. If you use this type, your iOS 8 button will behave like the system type.

Control States

When creating and configuring the button’s title, image, background image, and background color, you must consider the various states the button (control) can be in. The UIControl’s enabled, highlighted, and selected properties combine to form a single state value (UIControlState) for that control. The state will always be one of normal, highlighted, disabled, or selected.

When the button is displaying normally, its state is .Normal. When the user is touching it, its state changes to .Highlighted. When it’s disabled, its state becomes .Disabled.

When you set a button’s title, image, background, or color, you do so for a particular state. This allows you to set one button image for when the button is enabled and alternate images for when it’s disabled, highlighted, or selected. You see this reflected in the functions that set these properties:

setTitle(_:,forState:)
setTitleColor(_:,forState:)
setAttributedTitle(_:,forState:)
setImage(_:,forState:)
setBackgroundImage(_:,forState:)

You don’t have to set values for every state. At a minimum, set the value for the normal (.Normal) state. If that’s all you set, that value will be used for all other states. If you then want it to have a different title, image, background, or color for one of the other states, set that too.

There are lots of other, subtler properties for fine-tuning your button’s look and feel. You can, for example, control the shadow thrown by the title text or change the position (inset) of the title, image, and background image. Read through the documentation for UIButton for all the available properties.

Button Code

You now know enough about button properties to take a peek at the button construction code in UICatalog. Up to now, you’ve created button objects using Interface Builder, which is fine—there’s nothing wrong with that. But you can also create any iOS object programmatically, as you’ve done with other objects like arrays and images. And you can take a hybrid approach, letting Interface Builder create an object while you then customize or adapt it in code.

The UICatalog app takes this last approach. Most objects are created in a storyboard and then tweaked in the code. The project is meticulously organized to help you locate these. Each example in the app, such as Buttons, has a storyboard scene (Buttons Scene) and a view controller (ButtonsViewController) with similar names, as shown in Figure 10-6.

image

Figure 10-6. Organization of UICatalog project

Click the ButtonsViewController.swift file and find the configureImageButton() function. It should look something like the following code:

func configureImageButton() {
imageButton.setTitle("", forState: .Normal)
imageButton.tintColor = UIColor.applicationPurpleColor()
let imageButtonNormalImage = UIImage(named: "x_icon")
imageButton.setImage(imageButtonNormalImage, forState: .Normal)
imageButton.accessibilityLabel = NSLocalizedString("X Button", comment: "")
imageButton.addTarget(self, action: "buttonClicked:", forControlEvents: .TouchUpInside)
}

A basic button object is created in the storyboard and connected to the imageButton outlet. The configureImageButton() function then customizes it by clearing its title, assigning an image (for all states), adding explanatory text for the visually impaired, and connecting the button’s action to the buttonClicked(_:) function.

If you want to try different button properties to see what they look like, fiddle with this code and run the app again. The UICatalog app is your UI playground.

Switches and Sliders

Next up on the tour are switches and sliders. Both are input devices. Unlike buttons, switches and sliders retain a value. A switch is just that, as shown on the left in Figure 10-7. It presents a sliding button that can change between on and off values by either tapping or swiping it with your finger. You see these everywhere in iOS, as shown on the right in Figure 10-7.

image

Figure 10-7. Switches, sliders, and the Settings app

A UISwitch object has a single Boolean value property named, appropriately enough, on. Getting that property will tell you which position the switch is in. Setting the property changes it. You can request that UISwitch perform a little animation eye-candy, when you change its value programmatically, by calling the setOn(_:,animated:) function and passing true for the animated parameter.

A number of properties let you customize your switch’s appearance.

· tintColor, onTintColor, and thumbTintColor: The first two set the colors used when the switch is off and on. The thumbTintColor makes the “thumb” of the switch (the circle you drag) a different color than tintColor.

· onImage and offImage: Normally a switch displays either (localized) “On” or “Off” text titles next to the thumb. You can replace these with images of your choosing. There are important size restrictions, so read the documentation.

Like most controls with a value, a switch sends a “value changed” event (UIControlEvents.ValueChanged) whenever the user changes it. Connect this event to your controller to receive an action message when the switch is flipped.

Sliders, as shown in the middle of Figure 10-7, are another input control that lets the user choose a value by dragging a slider to a position within a predetermined range. While a switch’s value is Boolean, a slider’s value property is a floating-point value that represents a continuous range of numbers.

The slider’s value property is constrained to the range set by the minimumValue and maximumValue properties. These default to 0.0 and 1.0, respectively. Unless you change those, value will be a fractional number between 0.0 and 1.0 (inclusive).

The key visual customization properties are as follows:

· minimumTrackTintColor, maximumTrackTintColor, and thumbTintColor: These change the colors of the tracks (to the left and right of the thumb), as well as the thumb itself. See the tinted slider in the UICatalog (Figure 10-7) for an example and the code that does it.

· minimumValueImage, maximumValueImage, thumbImage (per state): Like with the slider, you can change the image used to draw the tracks of the slider and the thumb itself. The thumb image works like UIButton’s image, in that you can supply different images for different states (normal, highlighted, and disabled).

A slider sends a single “value changed” event when the user moves it, unless you set the continuous property to true. If you set continuous, the control fires a barrage of “value changed” messages as the user drags the slider. You used this setting in the ColorModel app in Chapter 8so color changes happened “live” as you dragged around a slider.

Page Control

A page control (UIPageControl) object, as shown at the bottom of Figure 10-8, can be thought of as a discrete slider control. As its name implies, it’s intended to indicate the user’s position within a small (up to 20) number of pages or items. Apple’s Weather app uses it to indicate which location the user is currently viewing, as shown on the right in Figure 10-8.

image

Figure 10-8. Page control and Weather app

UIPageControl’s integer currentPage property is its value, and its numberOfPages property determines the former’s range and the number of dots that appear. Its appearance can be slightly modified with these properties:

· pageIndicatorTintColor: This sets the color for the page indicator.

· hidesForSinglePage: If set to true, the control doesn’t draw anything if there’s only one page (numberOfPages<=1).

Tapping a page control object to the right or left of the current page either decrements or increments the currentPage property (moving forward or backward one page) and sends a “value changed” event.

Steppers

A stepper (UIStepper) has the face of UIButton and the heart of UIPageControl, as shown in Figure 10-9. It displays two buttons side by side. Use a stepper when your user needs to increase or decrease something—“something” is up to you; the stepper doesn’t display a value—one step at a time.

image

Figure 10-9. Stepper

Like a slider, the stepper’s minimumValue and maximumValue properties set the range for its value property. The stepValue property determines what “one step” means. As an example, Table 10-1 would be the property values you’d set for a stepper with 11 possible values, between 1.0 and 6.0.

Table 10-1. Property Values for a Stepper with 11 Possible Values (1.0 to 6.0, Inclusive)

Property

Value

minimumValue

1.0

maximumValue

6.0

stepValue

0.5

A stepper’s visual appearance can be customized using increment, decrement, and background images, which you set the same way you do for a button. There’s also a UIButton-ish tintColor property.

A stepper sends a “value changed” action every time the user taps the increment or decrement button. There are three properties that alter this behavior.

· continuous: The continuous property works just like it does for the slider.

· autorepeat: Setting autorepeat to true allows the user to continuously change the value (one step at a time) by holding down one of the buttons.

· wraps: This property lets the value “wrap” around the range. Using the example in Table 10-1, tapping + when the value was already a 6.0 would change the value back to 1.0. When wraps is true, the buttons do not disable when the value is at the beginning or end of the range.

Segmented Controls

Closely related to steppers is the UISegmentedControl class. A segmented control displays multiple segments. Each segment acts as a button for a choice, as shown in Figure 10-10. Use a segmented control when you want the user to pick between a small number of mutually exclusive choices.

image

Figure 10-10. Segmented controls

To use a segmented control, first tell it how may segments there are by setting the numberOfSegments property. You can then set the label of each segment to either a string title or an image using one of these functions:

setTitle(_:,forSegmentAtIndex:)
setImage(_:,forSegmentAtIndex:)

Alternatively, you can choose to insert (or remove) segments one at a time. Using these functions, you have the option of having the view animate the change, sliding and resizing the other segments to make room.

insertSegmentWithTitle(_:,atIndex:,animated:)
insertSegmentWithImage(_:,atIndex:,animated:)

A segmented control sends a “value changed” event (UIControlEvents.ValueChanged) when the user changes it. Its selectedSegmentIndex property tells you which segment is selected or can be used to change that. The special value UISegmentedControlNoSegmentmeans no segment is selected.

Normally, the buttons in a segment are “sticky”—they stay highlighted to indicate which segment is selected. If you set the momentary property to true, buttons don’t stay down, and the selectedSegmentIndex goes back to UISegmentedControlNoSegment when the user removes their finger.

Progress Indicators

iOS provides two progress indicators, UIActivityIndicatorView and UIProgressView, that provide your users with feedback during time-consuming activities or to display relative quantities (such as the amount of storage used), as shown in Figure 10-11. Use these to let your user know that your app is hard at work; they should remain calm and stay in their seats, with their seatbelts securely fastened.

image

Figure 10-11. Activity and progress indicator

The UIActivityIndicatorView is often called a spinner or gear. Use it when space is limited or the duration of the activity is indeterminate. There are three spinner styles (UIActivityIndicatorViewStyle) to choose from: small gray (.Gray), small white (.White), and large white (.WhiteLarge).

Using a spinner is easy. Call startAnimating() to start it spinning, and call stopAnimating() to stop it again. Its hidesWhenStopped and color properties are self-explanatory.

The second indicator is the progress bar (UIProgressView), shown on the right in Figure 10-11. It presents a progress indicator, familiar to anyone who’s had days of their life siphoned away waiting for computers to finish something. The view has two looks (UIProgressViewStyle).

· .Default: The regular style progress bar

· .Bar: A style designed to be used in a toolbar

You control the view by periodically setting its progress property to a value between 0.0 (empty) and 1.0 (full). Setting the progress property makes the indicator jump to that position. By calling setProgress(_:,animated:), you can ask the indicator to smoothly animate to the new setting, which is less jarring for big changes.

Use the trackImage or trackTintColor properties to customize the look of the unfinished segment of the view, and use the progressImage and progressTintColor properties to adjust the finished segment. In the UICatalog project, tint colors are set in theconfigureTintedProgressView() function, if you’d like to try some different colors.

Text Views

There are three species of text views in iOS: labels, text fields, and text views. The label is the simplest. It’s used to place a single string of text in your interface, often next to another field or view to explain its purpose, which is where it gets its name. A text field is a general-purpose input field, providing full-featured editing for a single line of text. A text view can display, and edit, multiple lines of text. Let’s start with the simple one.

Labels

You see labels everywhere in iOS (see practically any figure in this book), and you’ve used them numerous times in your own projects. Use a UILabel object wherever you simply want to display some text, for whatever purpose. Use label objects as labels by setting their text in Interface Builder and forgetting about them—no connection required. If you connect the label to an outlet, your controller can update the text, as you did in the ColorModel app.

Labels have a number of properties that let you alter how the text string is displayed, as listed in Table 10-2.

Table 10-2. Label Display Properties

Property

Description

numberOfLines

The maximum number of lines the label will display, normally 1. Set it to 0 to display as many lines as are needed.

font

The text’s font (face, size, and style).

textColor

The color used to draw the text.

textAlignment

One of left, center, right, justified, or natural. Natural employs the native alignment of the font.

attributedText

Draws an attributed string, instead of the simple text property. Use this to display text with a mixture of different fonts, sizes, styles, and colors. The text attributes in the string override the other text style properties (font, textColor, shadowOffset, and so on).

lineBreakMode

Determines how an overly long string is made to fit in the available space of the view.

adjustsFontSizeToFitWidth

An alternative to shortening the string; it makes the text smaller so the whole string will fit.

adjustLetterSpacingToFitWidth

A third option to get a string to fit within the given space.

If you plan on displaying a variable amount of text, pay attention to the properties that control what happens when the string is too big to fit in the view. First are the numberOfLines and lineBreakMode properties. The line break mode (NSLineBreakMode) controls how the string is broken up across multiple lines. The choices for multiple-line labels (numberOfLines!=1) are to break text at the nearest character (.ByCharWrapping) or at the nearest word (.ByWordWrapping).

For single-line labels (numberOfLines==1), text that won’t fit in the view is unceremoniously cut off (.ByClipping), or a portion of the beginning (.ByTruncatingHead), middle (.ByTruncatingMiddle), or end (.ByTruncatingTail) of the string is replaced by an ellipsis (...) character.

Note If the label’s width is not fixed by constraints, changing its text will alter its intrinsic size. The auto-layout logic will then attempt to resize the label view. If you let a label resize itself to fit its text, the lineBreakMode and related properties have no effect.

The alternate method of getting text to fit is to set the adjustsFontSizeToFitWidth or adjustLetterSpacingToFitWidth property to true. These options cause either the spacing between words or the size of the font—you can also set both—to be reduced in an attempt to make the string fit in the available space. The spacing between words will never be reduced to nothing, and its size will never be shrunk below the minimumScaleFactor property. If the text still won’t fit, the lineBreakMode is applied.

Caution Do not set adjustsFontSizeToFitWidth or adjustsLetterSpacingToFitWidth for a multiline (numberOfLines!=1) label or in conjunction with a multiline line break mode (char or word wrapping). Doing so is a programming error, and the behavior of the view will be unpredictable.

Text Fields

Use a text field (UITextField) when you want the user to enter or edit one line of text. The Shorty app used a text field to get an URL from the user.

The UICatalog app demonstrates five different text fields, as shown in Figure 10-12. Consistent with the complexity of editing almost any kind of text, you have a broad range of choices when it comes to the view’s appearance and behavior.

image

Figure 10-12. Text fields

Let’s start with the appearance of the field. There are four styles (UITextBorderStyle) to choose from, controlled by the borderStyle property.

· .RoundedRect: Draws a simple rounded rectangle around the field

· .Line: Draws a thin gray rectangle around the field

· .Bezel: Surrounds the field with a chiseled border, giving the illusion of the field being inset

· .None: Does not draw a border

The UICatalog app demonstrates only the rounded rectangle and none styles, but the other two aren’t hard to imagine. You can provide a more dramatic look by setting the background property to your own UIImage. The background property overrides the borderStyle property. In other words, you can’t supply a background image for a chiseled border; if you want that look, your image will need to include a chiseled border.

The placeholder property shows a string (in light gray) when the field is empty (see Figure 10-12). Use this to prompt the user (like “Your Name Here”) or possibly show a default value. Set the clearsOnBeginEditing to true if you want the text in the field to be automatically cleared before the user begins typing.

The font face, size, style, and color of the text in the field can be controlled either by setting the font and textColor properties or by using attributed text strings. The latter is considerably more complicated and commensurately more flexible.

You can also insert accessory views in three different places. Use these to add additional controls or indicators, such a button that pops up a set of options for the field or a progress indicator. The accessory view properties are as follows:

· leftView and leftViewMode

· rightView and rightViewMode

· inputAccessoryView

The left and right views can be any UIView object that will fit inside the text field. The UICatalog app demonstrates this by placing a purple UIButton in the rightView of the custom text field (see Figure 10-12) and inserting a small (invisible) UIView in the leftView; the latter is just used to create some padding on the left. The appearance of both right and left views are controlled by their companion rightViewMode and leftViewMode properties. Each can be set to never display the view, always display the view, display the view only when editing, or display the view only when not editing.

The input accessory view doesn’t get attached to the text field. Instead, it gets attached to the top of the virtual keyboard that appears when the user begins editing. You can use an input accessory view to add special controls, presets, options, and so on, to your user’s keyboard.

Text fields send a variety of events. The most useful are the “editing did end on exit” event (.EditingDidEndOnExit), which is sent when the user stops editing a field, and the “value changed” event (.ValueChanged), which is sent whenever the text in the field is modified. You connected actions to both of these events in the MyStuff app. To receive even more editing-related messages and exert some control over editing, create a delegate object for the text field (UITextFieldDelegate). The delegate receives messages when editing begins and ends, and it can also control if editing is allowed to begin, editing is allowed to end, or a specific change is allowed to be made.

Text Editing Behavior

There are a dizzying number of properties that affect how text in a field is edited. If you look in the documentation for UITextField, you won’t find any of them. That’s because they are defined in the UITextInput and UITextInputTraits protocols, which UITextField andUITextView both adopt. The number of properties and options are almost overwhelming, so I’ve listed the highlights in Table 10-3.

Table 10-3. Important Text-Editing Propertiesxs

Property

Description

autocapitalizationType

Controls the auto-capitalization mode: off or capitalize sentences, words, or characters.

autocorrectionType

Turns auto-correction on or off.

spellCheckingType

Turns spell checking, suggestions, and dictionary lookup on or off.

keyboardType

Chooses the virtual keyboard to use (normal, URL, just numbers, telephone dial, e-mail address, Twitter, and so on).

returnKeyType

If the keyboard has a “go” key, this property determines how it’s labeled: Go, Google, Join, Next, Route, Search, Send, Yahoo!, Done, or Emergency Call.

secureEntry

Hides the characters as the user types them to discourage onlookers from seeing the contents. Set this option for sensitive information, such as passwords.

Text Views

Text view (UITextView) objects are the synthesis of labels and text fields, as shown in Figure 10-13. UITextView is not a subclass of either, but it essentially inherits the capabilities of both and adds a few extra features of its own. Whether a text view can be edited is controlled by itseditable property.

image

Figure 10-13. A text view

With editable set to false, a text view acts much like a multiline label. It displays multiple lines of text in a variety of fonts, sizes, and styles, with control over line breaks. To this it adds some additional talents: scrolling, selection, and data detectors.

Unlike a label, if the text won’t fit in the vertical space of view, the user can scroll the text in the view to see the rest of it. You used this feature in the Surrealist app.

The user can select text in a text view (by touching and holding on the text), unless the selectable property is false. The selected text can be copied to the clipboard or used to look up words in the dictionary. In addition, you can enable data detectors. Data detectors are a technology that recognizes the purpose of certain text (such as a telephone number or someone’s e-mail address). The user can then tap the text to do something useful (place a phone call, address a new e-mail message, and so on).

With the editable property set to true, the text view becomes a (miniature) word processor. The user can type, select, cut, copy, and paste to their heart’s content. All of the editing features and options described in the “Text Fields” section apply to text views. About the only thing missing are the borders; text views do not draw a border.

A text view is also capable of editing styled (attributed) text, but you’ll have to supply the additional user interface elements that allow the user to choose a font face, size, style, color, and so on. The text view will handle the mechanics of applying those styles to what the user is typing, but your controller will have to tell the text view what those styles are.

Use a text view, instead of a label or text field, when

· The user needs to edit multiline text

· There’s more text than will fit in the view and you want it to scroll

· You want the user to have the ability to select and copy text or look up definitions

· You want to use data detectors

Search Bars

There are also specialty text fields. The UISearchBar view presents a text field specifically designed for performing searches. The UICatalog app neatly demonstrates the common ways you can embed a search bar into your design. I’ll summarize your options here.

You can use a UISearchBar on its own, placing it in any view. The first example demonstrates the standard search bar look and feel (see the middle image of Figure 10-14). The basic search bar contains an embedded text field, prepopulated with a search icon (so the user knows what it is) and a cancel button.

image

Figure 10-14. Stand-alone search bar views

You can optionally display a segmented control below the search field, allowing the user to choose from a set of options. Enable this by setting the showScopeBar property to true and then setting two or more button titles in the scopeButtonTitles property.

The second demonstration, shown on the right in Figure 10-14, shows some ways in which the search field’s presentation can be altered with custom backgrounds, different search bar styles (UIBarStyle), colored buttons, and so on.

The next three demonstrations show how to incorporate a search bar into a navigation bar or table view. iOS 8 adds several new ways of seamlessly integrating a search bar into these other elements.

First up, present the search bar on demand. In this technique, the search bar remains hidden (as shown in the upper left in Figure 10-15) until the user taps the search icon. The search bar is then presented over the navigation bar, replacing it until dismissed (see the lower-left image in Figure 10-15). The advantage is that your search feature doesn’t consume any appreciable amount of screen real estate until the user wants it. The disadvantage is that, once summoned, it obscures your navigation bar until it is dismissed.

image

Figure 10-15. Search bars in navigation bars and table views

The second technique embeds the search bar right into the navigation bar so it is always visible, as shown in the middle of Figure 10-15. The search bar is readily apparent and immediately available but permanently displaces the title of your navigation bar.

The last example demonstrates how to embed a search bar into a table view, instead of a navigation bar, shown on the right of Figure 10-15. Use this to present a persistent search bar without interfering with the navigation bar or in the absence of a navigation bar.

Like UIPickerView, a UISearchBar is not a UIControl subclass and does not send action messages. You should create a UISearchBarDelegate object that will receive the user’s input and related events.

Pickers

A picker is a user interface that lets the user choose something from a bounded set of choices. You used the image picker in your MyStuff to choose a picture from the photo library, and you used a media picker to choose a song from the iTunes library in DrumDub. These are both big interfaces that take over the entire user experience.

iOS also supplies a couple of smaller picker view objects. There’s the specialty UIDatePicker, for choosing dates and times, and the customizable UIPickerView, for anything else. Both present a view containing a number of vertical “wheels” that the user spins to choose the value or item they want, as shown in Figure 10-16.

image

Figure 10-16. Picker views

Date Picker

Use the date picker when you want the user to choose a date, time, or duration. The date picker has four different interfaces, controlled by its datePickerMode property. This can be set to one of the four values listed in Table 10-4.

Table 10-4. Date Picker Modes (UIDatePickerMode)

Mode

Description

.Time

Choose a time of day.

.Date

Choose a calendar date.

.DateAndTime

Choose a date and time.

.CountDownTimer

Choose a duration (hours and minutes).

The picker’s date property reports the value the user has selected. Setting it changes the date/time in the view. If you want to set the date and have the “wheels” spin to their new positions, call setDate(_:,animated:). The time portion of the date property is 0:00 when using the date-only interface. Similarly, the calendar day of the date property is meaningless when using the time-only or duration interface.

If you want to limit the range of values the user can choose from, set the minimumDate and/or maximumDate properties. For example, to force the user to choose a day in the future, set minimumDate to tomorrow.

You can also reduce the granularity of time choices with the minuteInterval property. When set to 1, the user can choose any time or duration in one-minute increments (2:30, 2:31, 2:32, and so on). Setting minuteInterval to 5 narrows the user’s choices to five-minute intervals (2:30, 2:35, 2:40, 2:45, and so on).

Caution The value of minuteInterval must divide evenly into 60 and can’t be more than 30.

If you plan on using a date picker and your interface leaves the picker visible while time progresses, Apple recommends updating the picker in real time. For example, if your interface uses a duration picker and a start button, pressing the start button will probably cause some timer in your app to begin counting down. During that time, your app should periodically update the picker so it slowly (once a minute) changes as the time counts down to zero.

Anything Picker

What if you don’t need to pick a date or a time? What if you need to pick an ice cream flavor, a model of car, or an arch nemesis? The UIPicker object is the catchall picker view. It looks and functions much like the date picker, except that you define the wheels and the content of each (shown on the right in Figure 10-16). This custom picker in UICatalog allows the user to “dial in” a color by selecting a value for the red, green, and blue color components, each on its own wheel.

A UIPicker uses a delegate and data source arrangement that’s eerily similar to a table view (Chapter 5). A UIPicker needs a delegate object (UIPickerDelegate) and a data source object (UIPickerDataSource). The picker’s data source determines the number of wheels (called components) and the number of choices (called rows) on each wheel. The delegate object provides the label for each choice. At a minimum, you must implement these UIPickerDataSource functions:

numberOfComponentsInPickerView(_:) -> Int
pickerView(_:,numberOfRowsInComponent:) -> Int

And you must implement one of these UIPickerDelegate functions:

pickerView(_:,titleForRow:,forComponent:) -> String
pickerView(_:,attributedTitleForRow:,forComponent:) -> NSAttributedString
pickerView(_:,viewForRow:,forComponent:,reusingView:) -> UIView

Tip Most often, a single object is both the delegate and the data source for a picker, in which case the division of functions between the two protocols doesn’t matter.

The first data source function tells your picker how many wheels it has. The second function is then called once for each wheel; it returns the number of rows in that wheel.

Finally (much like the table view data source), a delegate function returns the label for each row in each wheel. You have three choices for which function you implement, depending on how sophisticated you want to be with the content of each row.

· Implement pickerView(_:,titleForRow:,forComponent:) to show plain-text labels. Your function returns a simple string value for each row. This is the most common.

· Implement pickerView(_:,attributedTitleForRow:,forComponent:) to display labels containing special fonts or styles. Your function returns an attributed string for each row, allowing each to have a mixture of fonts, sizes, and styles. This is the technique used in UICatalog. It cleverly creates an attributed string with a text color that matches the intensity of the color component for that row.

· Implement pickerView(_:,viewForRow:,forComponent:,reusingView:) to display anything you want in a row. Your function returns a UIView object, which is then used to draw that row.

The last function is the most like the table view’s use of cell objects. For a picker, you can supply a different UIView object for each row or reuse a single UIView object over and over again. There’s no row cell object cache like in the table view. Instead, the last UIView returned is passed to your delegate the next time pickerView(_:,viewForRow:,forComponent:,reusingView:) is called. If you’re reusing a single UIView object, alter that view and return it again. If not (or the view parameter is nil), return a new view object.

If you want to control the width of each wheel or the height of the rows in a wheel, implement the optional pickerView(_:,widthForComponent:) or pickerView(_:,rowHeightForComponent:) function, respectively.

UIPickerView objects are not control objects; they are not subclasses of UIControl, and they don’t send action messages. Instead, the picker’s delegate gets a pickerView(_:,didSelectRow:,inComponent:) function call when the user changes one of the wheels.

Image Views

You’ve already used enough image views to know your way around them. There are, however, a couple of properties that I’d like to mention. The first is the contentMode. This property controls how the image (which may not be the same size as the view) gets arranged. The choices are listed in Table 10-5.

Table 10-5. View Content Mode

Mode (UIViewContentMode)

Description

.ScaleToFill

Stretches or squeezes the image to exactly fill the view. It may distort the image if the aspect ratio of the view is not the same as the image.

.ScaleAspectFit

Scales the image, without distorting it, so it just fits inside the view. Some parts of the view may not contain any image (think letterboxing).

.ScaleAspectFill

Scales the image, without distorting it, so it completely fills the view. Some parts of the image may get clipped.

.Center

Centers the image without scaling it.

.Top, .Bottom, .Left, or .Right

The middle of one edge of the image is aligned with the corresponding edge of the view. The image is not scaled. The image may not fill, or be clipped, in the other three directions.

.TopLeft, .TopRight, .BottomLeft, or .BottomRight

One corner of the image is aligned with the same corner of the view. The image is not scaled. The image may not fill the entire view or will be clipped if it overfills it.

Note The contentMode property is actually defined in the UIView class, but it’s particularly germane to UIImageView.

UIImageView also has a quirky talent: it can show a sequence of images either quickly (like a flipbook or a really short movie) or slowly (like a slideshow), as shown in Figure 10-17. Put the images you want to display into an array and use that to set the animationImages property. Set the animationDuration and, optionally, the animationRepeatCount to control the speed of each frame and how many times the entire sequence plays. (Set animationRepeatCount to 0 to play forever.)

image

Figure 10-17. Image view slideshow

Once set up, call the view’s startAnimation() function to begin the show, and call stopAnimation() to stop it again. Code that demonstrates this is in the configureImageView() function that you’ll find in the ImageViewController.swift file.

Grouped Tables

Chapter 5 mentioned that you can create grouped table views, like those used in the Settings app. I didn’t, however, actually show you how to do that. You already have all of the basics, but if you want a concrete example, look no further than the UICatalog project. Many of the sample views (activity indicators, buttons, text fields, and segmented controls) are presented in a group table view. Each group is a single example.

The Main.storyboard file in UICatalog uses the static table cell method I mentioned in Chapter 5. If you look at the table view for the Text Fields Scene, as shown in Figure 10-18, you’ll see that all of the cells and their groups have been designed right in Interface Builder.

image

Figure 10-18. Statically designed table with groups

It’s also possible to build a grouped table programmatically. Your table delegate simply needs to provide the group information, much the way it provides the row count and cells. Here are the delegate functions you’ll need to write:

numberOfSectionsInTableView(_:) -> Int
tableView(_:,titleForHeaderInSection:) -> String
tableView(_:,numberOfRowsInSection:) -> Int

The View You Never See

That wraps up most of the important view objects in iOS. I’ll talk about toolbars a little in Chapter 12 and a lot more about UIView in Chapter 11. But I want to mention a special view—one that’s used a lot but you never see.

It’s the UIScollView class. A scroll view adds the dynamics of scrolling to your interface. You never see the scroll view; you see its effects. A scroll view works by presenting a larger view inside a smaller view. The effect is like having a window into that larger view. When you drag inside the window, you are “sliding” the view behind it around so you can see different portions of it, as illustrated in Figure 10-19.

image

Figure 10-19. Conceptual arrangement a scroll view

It’s easiest to think about a scroll view as being two views in one. For most view objects, the size of its content (called its bounds) and the size it occupies in your interface (called its frame) are the same. So, a view, say a button, that’s 30 by 100 pixels will occupy a region of 30 by 100 pixels in your interface.

A scroll view breaks this relationship. A scroll view has a special contentSize property that’s divorced from its frame size. Its frame becomes the “window” that appears in your interface. The contentSize defines the logical size of the view, only a portion of which is visible through the window.

The contentOffset property determines exactly what portion is visible. This property is the point in the content area that appears at the upper-left corner of the frame—the portion visible to the user. contentOffset is initially 0,0. This places the upper-left corner of the content at the upper-left corner of the frame. As the contentOffset moves down, the content appears to scroll up, keeping the contentOffset point at the upper-left corner of the frame.

Table views, web views, and text views all provide scrolling and are all subclasses of UIScrollView. You can subclass UIScrollView yourself to create a custom view that supports scrolling, or you can use a UIScrollView object on its own by simply populating its content view with whatever subviews you like. You can even have a scroll view inside another scroll view; it’s weird, but there are notes in the Scroll View Programming Guide for iOS on how to do it.

A great place to get started with scroll views is the PhotoScroller example project. (As of this writing, this project is still in Objective-C.) Search Xcode’s Documentation and API Reference for the PhotoScroller sample code project and click the Open Project button. The PhotoScroller project defines a subclass of UIScrollView used to display, pan, and zoom an image. This project demonstrates two of scroll view’s three major talents.

· Scrolling a larger content view around inside a smaller view

· Pinching and zooming the content view

· Scrolling by “page”

The first is its basic function. It’s for this ability that scroll views are most often used, which includes table views, web views, and text views. To use a scroll view in this fashion, you don’t have to subclass it or use a delegate. Simply populate and size its content view with the views you want to display, and the scroll view will let the user drag it around.

The scroll view’s second talent is pinching and zooming its content view, so it not only scrolls it but magnifies and shrinks it as well, as shown in Figure 10-20. This feature requires the use of a scroll view delegate (UIScrollViewDelegate) object. In the PhotoScroll project, the customImageScrollView is a UIScrollView subclass that’s also its own delegate—an arrangement that’s perfectly legitimate, if a little unusual. UIScrollView processes the touch events and handles the most of the panning and zooming details for you.

image

Figure 10-20. PhotoScroller app

You can also cause the view to scroll programmatically by setting its contentOffset property to any point in the content view you want. If you want to make the view animate its journey to the new position, call the setContentOffset(_:,animate:) function.

SCROLL VIEWS AND THE KEYBOARD

Scroll views can contain text fields—usually indirectly by placing a text field in a table view, which you now know is a scroll view. When the keyboard appears, it can cover up the very text field the user wants to edit. The solution is to cause the scroll view to scroll up so the text field is visible above the keyboard.

To do that, your controller will need to observe keyboard notifications (such as UIKeyboardDidShowNotification). These notifications contain the coordinates of the virtual keyboard on the screen. You use this information to determine whether the keyboard is covering your text field. If it is, use the scroll view’s setContentOffset(_:,animate:) function to cause the text field to scroll to a position above the virtual keyboard.

The mechanics of this is described in the Text, Web, and Editing Programming Guide for iOS, which you’ll find in Xcode’s documentation. Look for the aptly named section “Moving Content That Is Located Under the Keyboard” in the “Managing the Keyboard” chapter.

The PhotoScroller project also demonstrates an advanced technique called tiling. In the beginning of Chapter 5, I explained that an iOS device doesn’t have enough memory or CPU power to create thousands of individual row objects for a table. Instead, it draws just the portion of the table that is visible to the user, as the user scrolls through the list.

Any exceptionally large content view may fall into the same category. The PhotoScroller project demonstrates how to dynamically prepare only those view objects that are currently visible through the scroll view’s “window.” The table view—which, as you remember, is based on UIScrollView—already does this, preparing the view objects only for those rows that are visible in the table.

A much less common use of scroll views is to view content in “pages.” This is enabled by setting the pagingEnabled property to true. When you do that, the scroll view forces the content view (technically, its contentOffset property) to move in discrete distances, exact multiples of its frame size. Conceptually, it divides your content view into a grid (the exact size of the window), and any scrolling eventually settles on one segment. There’s a PageControl sample project that demonstrates this feature.

Note The PhotoScroller project lets you swipe between images, but it’s not using UIScrollView’s paging feature. Instead, it uses a UIPageViewController. You’ll use UIPageViewController to create a similar interface in Chapter 12.

Advanced use of scroll views is not for the faint of heart. This can be really complex stuff, but it’s the stuff of really cool apps. The now-famous “drag to update” gesture that has become the mainstay of iOS apps is all done with scroll views and scroll view delegates. If you need this feature in a table view, most of the work is already done for you; create a UIRefreshControl object and connect it to the table view controller’s refreshControl property. Now the user can drag down to update the table. To dive into the power of scroll views, start with the Scroll View Programming Guide for iOS.

Alert Controllers

Alert controllers are the odd duck in the UICatalog project. They’re out of place because they aren’t view objects; they’re view controllers. You have an exciting chapter on view controllers coming up (Chapter 12), so I really should defer alert controllers until then.

But, I won’t. The reason is that alert controllers—while they are view controllers—are so specialized that you typically don’t use, present, or adapt them the way you would other view controllers. So, while you have the UICatalog project running, let’s explore how you use it right now.

An alert controller suspends the current interface and presents a choice, typically a button or two, that the user must tap to proceed. You can see examples in Figures 10-21 and 10-22. The user’s choice executes a block of code, which you provide, and the controller is dismissed. It’s that simple.

image

Figure 10-21. Alert examples

image

Figure 10-22. Alert and action sheet examples

UIAlertController is new in iOS 8 and replaces the UIAlertView and UIActionSheet classes, both now deprecated. This new class greatly simplifies, and unifies, the presentation of alerts and action sheets.

Note You might encounter various deprecated classes, properties, or methods. APIs evolve over time. When better, more modern features are introduced, these often replace older classes and functions. These older interfaces may be deprecated, which means that Apple now discourages their use in new development, and Apple may someday eliminate them entirely—at which time any apps you have that still use them will stop running, or at least compiling. The Swift language was introduced alongside iOS 8, and there are no Swift APIs for deprecated functions. In other words, you can’t write Swift code that uses an obsolete class or method from iOS 7 or earlier. So, for a brief period of iOS history, Swift programmers don’t have to worry about deprecated features because, until iOS 9 comes out, there are none.

Using an alert controller is easy, and you’ve already done it several times in this book. The steps are as follows:

1. Create a UIAlertController object, choosing a preferred style and optionally assigning it a title and message.

2. Create one or more UIAlertAction objects and add them to the controller.

3. Optionally, set any special properties of the controller or add text fields.

4. Present the controller.

The preferred style (UIAlertControllerStyle) determines how you would like the alert presented. You choices are .Alert or .ActionSheet. It’s a “preferred” style because, as you’ll see in Chapter 12, a presentation controller may decide to present it in a different way—but I’m getting ahead of myself.

The .Alert style (on a compact device) appears as a floating window, as shown in Figure 10-21 and on the left in Figure 10-22. The .ActionSheet style appears as a set of detached buttons at the bottom of the interface, shown on the middle and right of Figure 10-22.

· Use an alert when you need to get the user’s attention (“Something went horribly wrong!”), you need a confirmation to continue (“Are you sure you want me to delete all of your pictures?”), or you need to gather information (“What’s the password?”).

· Use an action sheet when offering a choice of possible actions (“Follow Obi Wan” or “Join the Dark Side”).

The title and message can be anything you want, and either can be omitted, but they should be short and to the point. A message is presented only in an alert and is ignored by action sheets.

Tip As a rule, try to avoid alerts and action sheets. An interface that doesn’t need them is almost always easier to use than one that does. They’re great in a pinch, but overuse of modal interfaces—like alerts—is the hallmark of a poor user interface.

Once you’ve decided how it will appear, you need to add some actions. A UIAlertAction object represents one choice in the interface. Each action object defines the text of the button that will appear, the type of choice, and a block of code to be executed if the user taps that button. You did this in your MyStuff app in Chapter 7, so let’s look at that code again.

let alert = UIAlertController(title: nil,
message: nil,
preferredStyle: .ActionSheet)
alert.addAction(UIAlertAction(title: "Take a Picture",
style: .Default,
handler: { (_) in
self.presentImagePicker(.Camera)
}))
alert.addAction(UIAlertAction(title: "Choose a Photo",
style: .Default,
handler: { (_) in
self.presentImagePicker(.PhotoLibrary)
}))
alert.addAction(UIAlertAction(title: "Cancel",
style: .Cancel,
handler: nil))
presentViewController(alert, animated: true, completion: nil)

This code follows the formula to the letter. It creates a UIAlertController object. The action sheet has no title because the choices are self-explanatory. It creates three actions buttons: Take a Picture, Choose a Photo, and Cancel. It then presents the view controller.

Each action has a style. Your choices are .Default, .Destructive, and .Cancel. The default style presents a normal button. The destructive style presents an angry red button that should be used only for choices with negative consequences.

The cancel style is special and should be assigned to the single action that does nothing (if there is one). A cancel action can have a handler block, but it’s not required if there’s nothing to do. In some circumstances (again, you’ll get to this in Chapter 12), the alert or action may be presented in a different form. For example, it could be presented in a popover on an iPad or similar device. In cases like that, the cancel button may be omitted because tapping outside a popover is an implied cancel.

Finally, there are a couple of special options you can set on the alert controller before presenting it. Most notably, an alert can also present one or more text entry fields, as shown on the left in Figure 10-22. You add these by calling theaddTextFieldWithConfigurationHandler(_:) function. The alert controller goes to the trouble of creating and inserting the text field, and then your configuration handler block is given the opportunity to make any additional tweaks that your app might need. Locate theshowSecureTextEntryAlert(_:) function in the AlertControllerViewController.swift file. You’ll see the following code:

let alertController = UIAlertController(title: title,
message: message,
preferredStyle: .Alert)
alertController.addTextFieldWithConfigurationHandler { textField in
textField.secureTextEntry = true
}

After creating the alert controller, it requests the addition of a text field. After the field is added, the configuration handler block sets its secureTextEntry property, making it suitable for a password or other sensitive text.

Summary

Your command of the “language” of iOS is growing. You started with the syntax and grammar of iOS, learning to create objects, connect them, and send messages. In this chapter, you expanded your vocabulary, acquiring an impressive number of view and control objects you can add and customize. You also saw how grouped tables are made and got a glimpse of the magic behind scrolling. In the process, you learned how to download sample code and unlock its secrets.

You can go a long way using premade view and control objects. But there are limits, and at some point you’re going to want a view that no one has created yet. Creating your own views is the next step of your journey and the next chapter in this book.