Nib Management - IDE - iOS 8 Programming Fundamentals with Swift (2014)

iOS 8 Programming Fundamentals with Swift (2014)

Part II. IDE

Chapter 7. Nib Management

In Chapter 4, I talked about ways of obtaining an instance. You can directly instantiate an object type:

let v = UIView()

Or you can obtain a reference to an already existing instance:

let v = self.view.subviews[0] as! UIView

But there is a third way: you can load a nib. A nib is a file, in a special format, filled with instructions for creating and configuring instances. To load a nib means, in effect, to tell that nib to follow those instructions: it does create and configure those instances.

My example of a UIView instance is apt, because a UIView is just the kind of instance a nib is likely to create. Nibs are edited in Xcode using a graphical interface, much like a drawing program. The idea is that you are designing some interface objects — mostly instances of UIView and UIView subclasses — that you want to use in your app when it runs. When your app does run, and when the moment comes where you actually need those interface objects (typically because you’re about to display them in your visible interface), you load the nib, the nib-loading process creates and configures the instances, and you receive the instances and insert them into your app’s actual interface.

You do not have to use nibs to create your interface objects. The loading of a nib does nothing that you could not have done directly, in code. You can instantiate a UIView or UIView subclass, you can configure it, you can construct a hierarchy of views, you can place that view hierarchy into your interface — manually, step by step, entirely in code. A nib is just a device for making that process simpler and more convenient. You design the nib beforehand, graphically; when the app runs, your code doesn’t have to instantiate or configure any views. It has merely to load the nib and retrieve the resulting instances and put them into your interface. And in fact, because you’ll mostly be using view controllers (UIViewController), which are themselves designed with nibs in mind, you won’t even have to do that! The view controller will load the nib and retrieve the resulting instances and put them into your interface for you, automatically.

Nibs are a simple and ingenious device for making the process of designing and configuring your app’s interface simpler and more convenient than it would be if you had to do the same thing in code. But they are also probably the least understood aspect of iOS programming. Many beginners discover nibs the first day they start programming iOS, and proceed to use nibs for years, without knowing what they really are or how they really work. This is a huge mistake. Nibs are not magic. They are not hard to understand. It is crucial that you know what nibs are, how they work, and how to manipulate them in your code. Failure to understand nibs opens you up to all kinds of elementary, confusing mistakes that can be avoided or corrected merely by grasping a few basic facts. Those facts are the subject of this chapter.

NOTE

The name nib, or nib file, has nothing to do with fountain pens or bits of chocolate; it originated as an acronym for “NeXTStep Interface Builder,” which was used for the .nib file extension. This, in turn, is because the graphical nib-design aspect of Xcode, which I call the nib editor, was itself (up through Xcode 3.2.x) a separate application called Interface Builder. (The nib editor environment within Xcode is still often referred to as Interface Builder.) Nowadays, the file you edit directly in the nib editor will be either a .storyboard file or a .xib file; when the app is built, such files are compiled into nib files (see Chapter 6).

ARE NIBS NECESSARY?

Since nibs are ultimately just sources of instances, you might wonder whether it is possible to do without them. Those same instances could be generated in code, so wouldn’t it be possible to dispense with nibs altogether? The simple answer is: Yes, it would. It’s quite possible to write a complex app that lacks a single .storyboard or .xib file (I’ve done it). The practical question, however, is one of balance. Most apps use nib files as a source of at least some interface objects; but there are some aspects of interface objects that can be customized only in code, and sometimes it’s easier to generate those interface objects entirely in code at the outset. In real life your projects will probably involve some code-generated interface objects and some nib-generated interface objects (which may themselves be further modified or configured in code).

A Tour of the Nib Editor Interface

Let’s explore Xcode’s nib editor interface. In Chapter 6, we created a simple iPhone project, Empty Window, directly from the Single View Application template; it contains a storyboard file, so we’ll use that. In Xcode, open the Empty Window project, locate Main.storyboard in the Project navigator, and click to edit it.

Editing a nib file

Figure 7-1. Editing a nib file

Figure 7-1 shows the project window after selecting Main.storyboard. (I’ve made some additional adjustments to make the screenshot fit on the page.) The interface may be considered in four pieces:

1. The bulk of the editor is devoted to the canvas, where you physically design your app’s interface. The canvas portrays views in your app’s interface and things that can contain views. A view is an interface object, which draws itself into a rectangular area. The phrase “things that can contain views” is my way of including view controllers, which are represented in the canvas even though they are not drawn in your app’s interface; a view controller isn’t a view, but it has a view (and controls it).

2. At the left of the editor is the document outline, listing the storyboard’s contents hierarchically by name. It can be hidden by dragging its right edge or by clicking the button at the bottom left corner of the canvas.

3. The inspectors in the Utilities pane let you edit details of the currently selected object.

4. The libraries in the Utilities pane, especially the Object library, are your source of interface objects to be added to the nib.

The Document Outline

The document outline portrays hierarchically the relationships between the objects in the nib. This structure differs slightly depending on whether you’re editing a .storyboard file or a .xib file.

In a storyboard file, the primary constituents are scenes. A scene is, roughly speaking, a single view controller, along with some ancillary material; every scene has a single view controller at its top level.

A view controller isn’t an interface object, but it manages an interface object, namely its view (or main view). A view controller in a nib doesn’t have to have its main view in the same nib, but it usually does, and in that case, in the nib editor, the view usually appears inside the view controller in the canvas. Thus, in Figure 7-1, the large highlighted rectangle in the canvas is a view controller’s main view, and is actually inside a view controller.

The view controller itself can be readily seen and selected in the document outline. It is also represented as an icon in the scene dock, which appears above the view controller in the canvas when anything in this scene is selected (Figure 7-2). Each view controller in a storyboard file, shown in the canvas together with its view and its scene dock, constitutes one scene. This scene is also portrayed as a hierarchical collection of names in the document outline. At the top level of the document outline are the scenes themselves. At the top level of each scene are (more or less) the same objects that appear in the view controller’s scene dock: the view controller itself, along with two proxy objects, the First Responder token and the Exit token. These objects — the ones displayed as icons in the scene dock, and shown at the top level of the scene in the document outline — are the scene’s top-level objects.

A view controller selected in a storyboard

Figure 7-2. A view controller selected in a storyboard

(New in Xcode 6.2 is the Storyboard Entry Point. This supersedes the older notion of a storyboard’s initial view controller, in preparation for a day when one storyboard will be able to “contain” another. If this view controller is the storyboard’s entry point, the Storyboard Entry Point icon is listed with it in the document outline.)

Objects listed in the document outline are of two kinds:

Nib objects

The view controller, along with its main view and any subviews that we care to place in that view, are real objects — potential objects that will be turned into actual instances when the nib is loaded by the running app. Such real objects to be instantiated from the nib are also called nib objects.

Proxy objects

The proxy objects (here, the First Responder and Exit tokens) do not represent instances that will come from the nib when it is loaded. Rather, they represent other objects, and are present to facilitate communication between nib objects and other objects (I’ll give examples later in this chapter). You can’t create or delete a proxy object; the nib editor shows them automatically.

Aside from the top-level objects, most objects listed in a storyboard’s document outline will depend hierarchically upon a scene’s view controller. For example, in Figure 7-2, the view controller has a main view; that view is listed as hierarchically dependent on the view controller. That makes sense, because this view belongs to this view controller. Moreover, any further interface objects that we drag into the main view in the canvas will be listed in the document outline as hierarchically dependent on the view. That makes sense, too. A view can contain other views (its subviews) and can be contained by another view (its superview). One view can contain many subviews, which might themselves contain subviews. But each view can have only one immediate superview. Thus there is a hierarchical tree of subviews contained by their superviews with a single object at the top. The document outline portrays that tree as an outline — hence the name.

In a .xib file, there are no scenes. What would be, in a .storyboard file, the top-level objects of a scene become, in a .xib file, the top-level objects of the nib itself. Nor is there any requirement that one of these top-level objects be a view controller; it can be, but more often the top-level interface object of a .xib file is a view. It might well be a view that is to serve as a view controller’s main view, but that’s not a requirement either. Figure 7-3 shows a .xib with a structure parallel to the single scene of Figure 7-2.

A .xib file containing a view

Figure 7-3. A .xib file containing a view

The document outline in Figure 7-3 lists three top-level objects. Two of them are proxy objects, termed Placeholders in the document outline: the File’s Owner, and the First Responder. The third is a real object, a view; it will be instantiated when the nib is loaded as the app runs. The document outline in a .xib file can’t be completely hidden; instead, it is collapsed into a set of icons representing the nib’s top-level objects, similar to a scene dock in a storyboard file, and often referred to simply as the dock (Figure 7-4).

The dock in a .xib file

Figure 7-4. The dock in a .xib file

At present, the document outline may seem unnecessary, because there is very little hierarchy; all objects in Figures 7-2 and 7-3 are readily accessible in the canvas. But when a storyboard contains many scenes, and when a view contains many levels of hierarchically arranged objects (along with their autolayout constraints), you’re going to be very glad of the document outline, which lets you survey the contents of the nib in a nice hierarchical structure, and where you can locate and select the object you’re after. You can also rearrange the hierarchy here; for example, if you’ve made an object a subview of the wrong view, you can reposition it within this outline by dragging its name. You can also select objects using the jump bar at the top of the editor: the last jump bar path component (Control-6) is a hierarchical pop-up menu similar to the document outline.

If the names of nib objects in the document outline seem generic and uninformative, you can change them. The name is technically a label, and has no special meaning, so feel free to assign nib objects labels that are useful to you. Select a nib object’s label in the document outline and press Return to make it editable, or select the object and edit the Label field in the Document section of the Identity inspector (Command-Option-3).

Canvas

The canvas provides a graphical representation of a top-level interface nib object along with its subviews, similar to what you’re probably accustomed to in any drawing program. The canvas is scrollable and automatically accommodates however many graphical representations it contains; a storyboard canvas can also be zoomed (choose Editor → Canvas → Zoom, or use the contextual menu).

(In a .xib file, you can remove the canvas representation of a top-level nib object, without deleting the object, by clicking the X at its top left — see Figure 7-3) — and you can restore the graphical representation to the canvas by clicking that nib object in the document outline.)

Our simple Empty Window project’s Main.storyboard contains just one scene, so it represents graphically in the canvas just one top-level nib object — the scene’s view controller. Inside this view controller, and generally indistinguishable from it in the canvas, is its main view. It happens that this view controller will become our app’s window’s root view controller when the app runs; therefore its view will occupy the entire window, and will effectively be our app’s initial interface (see Chapter 6). That gives us an excellent opportunity to experiment: any visible changes we make within this view should be visible when we subsequently build and run the app. To prove this, let’s add a subview:

1. Start with the canvas looking more or less like Figure 7-1.

2. Look at the Object library (Command-Option-Control-3). If it’s in icon view (a grid of icons without text), click the button at the left of the filter bar to put it into list view. Click in the filter bar (or choose Edit → Filter → Filter in Library, Command-Option-L) and type “button” so that only button objects are shown in the list. The Button object is listed first.

3. Drag the Button object from the Object library into the view controller’s main view in the canvas (Figure 7-5), and let go of the mouse.

Dragging a button into a view

Figure 7-5. Dragging a button into a view

A button is now present in the view in the canvas. The move we’ve just performed — dragging from the Object library into the canvas — is extremely characteristic; you’ll do it often as you design your interface.

Much as in a drawing program, the nib editor provides features to aid you in designing your interface. Here are some things to try:

§ Select the button: resizing handles appear. (If you accidentally select it twice and the resizing handles disappear, select the view and then the button again.)

§ Using the resizing handles, resize the button to make it wider: dimension information appears.

§ Drag the button near an edge of the view: a guideline appears, showing a standard margin space between the edge of the button and the edge of the view. Similarly, drag it near the center of the view: a guideline shows you when the button is centered.

§ With the button selected, hold down the Option key and hover the mouse outside the button: arrows and numbers appear showing the distance between the button and the edges of the view. (If you accidentally clicked and dragged while you were holding Option, you’ll now have two buttons. That’s because Option-dragging an object duplicates it. Select the unwanted button and press Delete to remove it.)

§ Control-Shift-click on the button: a menu appears, letting you select the button or whatever’s behind it (in this case, the view, as well as the view controller because the view controller acts as a sort of top-level background to everything we’re doing here).

§ Double-click the button’s title. The title becomes editable. Give it a new title, such as “Howdy!” Hit Return to set the new title.

To prove that we really are designing our app’s interface, we’ll run the app:

1. Drag the button to a position near the top left corner of the canvas. (If you don’t do this, the button could be off the screen when the app runs.)

2. Examine the Debug → Activate / Deactivate Breakpoints menu item. If it says Deactivate Breakpoints, choose it; we don’t want to pause at any breakpoints you may have created while reading the previous chapter.

3. Make sure the destination in the Scheme pop-up menu is iPhone 6.

4. Choose Product → Run (or click the Run button in the toolbar).

After a heart-stopping pause, the iOS Simulator opens, and presto, our empty window is empty no longer (Figure 7-6); it contains a button! You can tap this button with the mouse, emulating what the user would do with a finger; the button highlights as you tap it.

The Empty Window app’s window is empty no longer

Figure 7-6. The Empty Window app’s window is empty no longer

Inspectors and Libraries

Four inspectors appear in conjunction with the nib editor, and apply to whatever object is selected in the document outline, dock, or canvas:

Identity inspector (Command-Option-3)

The first section of this inspector, Custom Class, is the most important. Here you learn, and can change, the selected object’s class. Some situations in which you’ll need to change the class of an object in the nib appear later in this chapter.

Attributes inspector (Command-Option-4)

Settings here correspond to properties and methods that you might use to configure the object in code. For example, selecting our view and choosing from the Background pop-up menu in the Attributes inspector corresponds to setting the view’s backgroundColor property in code. Similarly, selecting our button and typing in the Title field is like calling the button’s setTitle:forState: method.

The Attributes inspector has sections corresponding to the selected object’s class inheritance. For example, the UIButton Attributes inspector has three sections: in addition to a Button section, there’s a Control section (because a UIButton is also a UIControl) and a View section (because a UIControl is also a UIView).

Size inspector (Command-Option-5)

The X, Y, Width, and Height fields determine the object’s position and size within its superview, corresponding to its frame property in code; you can equally do this in the canvas by dragging and resizing, but numeric precision can be desirable.

If autolayout is turned on (the default for new .storyboard and .xib files), the rest of the Size inspector has to do with the selected object’s autolayout constraints, plus the four buttons at the lower right of the canvas help you manage alignment, positioning, and constraints.

Connections inspector (Command-Option-6)

I’ll discuss and demonstrate use of the Connections inspector later in this chapter.

Two libraries are of particular importance when you’re editing a nib:

Object library (Command-Option-Control-3)

This library is your source for objects that you want to add to the nib.

Media library (Command-Option-Control-4)

This library lists media in your project, such as images that you might want to drag into a UIImageView — or directly into your interface, in which case a UIImageView is created for you.

TIP

I’ve now mentioned autolayout and constraints a couple of times, but I’m not going to explain here what they are. Nor am I going to discuss size classes and conditional constraints (the “Any” buttons at the bottom of the canvas). These are large topics having to do with views and view controllers, and are outside the scope of this book. I deal with these matters in full detail, including how to work with constraints and size classes in the nib editor, in Programming iOS 8.

Nib Loading

A nib file is a collection of potential instances — its nib objects. Those instances become real only if, while your app is running, the nib is loaded. At that moment, the nib objects contained in the nib are transformed into instances that are available to your app.

This architecture is a source of great efficiency. A nib usually contains interface; interface is relatively heavyweight stuff. A nib isn’t loaded until it is needed; indeed, it might never be loaded. Thus this heavyweight stuff won’t come into existence until and unless it is needed. In this way, memory usage is kept to a minimum, which is important because memory is at a premium in a mobile device. Also, loading a nib takes time, so loading fewer nibs at launch time — enough to generate just the app’s initial interface — makes launching faster.

Nib loading is a one-way street: there’s no such thing as “unloading” a nib. As a nib is loaded, its instances come into existence, and the nib’s work, for that moment, is done. Henceforward it’s up to the running app to decide what to do with the instances that just sprang to life. It must hang on to them for as long as it needs them, and will let them go out of existence when they are needed no longer.

Think of the nib file as a set of instructions for generating instances; those instructions are followed each time the nib is loaded. The same nib file can thus be loaded multiple times, generating a new set of instances each time. For example, a nib file might contain a piece of interface that you intend to use in several different places in your app. A nib file representing a single row of a table might be loaded a dozen times in order to generate a dozen visible rows of that table.

When Nibs Are Loaded

Here are some of the chief circumstances under which a nib file is commonly loaded while an app is running:

A view controller is instantiated from a storyboard

A storyboard is a collection of scenes. Each scene starts with a view controller. When that view controller is needed, it is instantiated from the storyboard. This means that a nib containing the view controller is loaded.

Most commonly, a view controller will be instantiated from a storyboard automatically. For example, as your app launches, if it has a main storyboard, the runtime looks for that storyboard’s initial view controller (entry point) and instantiates it (see Chapter 6). Similarly, a storyboard typically contains several scenes connected by segues; when a segue is performed, the destination scene’s view controller is instantiated.

It is also possible for your code to instantiate a view controller from a storyboard manually. To do so, you start with a UIStoryboard instance, and then:

§ You can instantiate the storyboard’s initial view controller by calling instantiateInitialViewController.

§ You can instantiate any view controller whose scene is named within the storyboard by an identifier string by calling instantiateViewControllerWithIdentifier:.

A view controller loads its main view from a nib

A view controller has a main view. But a view controller is a lightweight object (it’s just some code), whereas its main view is a relatively heavyweight object. Therefore, a view controller, when it is instantiated, lacks its main view. It generates its main view later, when that view is needed because it is to be placed into the interface. A view controller can obtain its main view in several ways; one way is to load its main view from a nib.

If a view controller belongs to a scene in a storyboard, and if, as will usually be the case, it contains its view in that storyboard’s canvas (as in our Empty Window example project), then there are two nibs involved: the nib containing the view controller, and the nib containing its main view. The nib containing the view controller was loaded when the view controller was instantiated, as I just described; now, when that view controller instance needs its main view, the main view nib is loaded automatically, and the whole interface connected with that view controller springs to life.

In the case of a view controller instantiated in some other way, there may be a .xib-generated nib file associated with it, containing its main view. Once again, the view controller will automatically load this nib and extract the main view when it’s needed. This association between a view controller and its main view nib file is made through the nib file’s name. In Chapter 6, we configured this association in code using the UIViewController initializer init(nibName:bundle:), when we said this:

self.window!.rootViewController =

MyViewController(nibName:"MyViewController", bundle:nil)

That code caused the view controller to set its own nibName property to "MyViewController". This, in turn, means that when the view controller needs its view, it gets it by loading the nib that comes from MyViewController.xib.

Your code explicitly loads a nib file

If a nib file comes from a .xib file, your code can load it manually, by calling one of these methods:

loadNibNamed:owner:options:

An NSBundle instance method. Usually, you’ll direct it to NSBundle.mainBundle().

instantiateWithOwner:options:

A UINib instance method. The nib in question was specified when UINib was instantiated and initialized with init(nibName:bundle:).

NOTE

To specify a nib file while the app is running actually requires two pieces of information — its name and the bundle containing it. And indeed, a view controller has not only a nibName property but also a nibBundle property, and the methods for specifying a nib, such as init(nibName:bundle:), have a bundle: parameter that allows you specify the bundle. In real life, however, the bundle will be the app bundle (or NSBundle.mainBundle(), which is the same thing); this is the default, so there will be no need to specify a bundle — and you’ll pass nil instead of supplying an explicit bundle.

Manual Nib Loading

In real life, you’ll probably configure your app so that most nib loading takes place automatically, in accordance with the various mechanisms and situations I’ve just outlined. But in order to understand the nib-loading process, it will be useful for you to practice loading a nib manually. Let’s do that.

First we’ll create and configure a .xib file in our Empty Window project:

1. In the Empty Window project, choose File → New → File and specify an iOS → User Interface → View file. This will be a .xib file containing a UIView instance. Click Next.

2. In the Save dialog, accept the default name, View, for the new .xib file. Click Create.

3. We are now back in the Project navigator; our View.xib file has been created and selected, and we’re looking at its contents in the editor. Those contents consist of a single UIView. It’s too large, so select it and, in the Attributes inspector, change the Size pop-up menu, under Simulated Metrics, to Freeform. Handles appear around the view in the canvas; drag them to make the view smaller.

4. Populate the view with some arbitrary subviews, dragging them into it from the Object library. You can also configure the view itself; for example, in the Attributes inspector, change its background color (Figure 7-7).

Designing a view in a .xib file

Figure 7-7. Designing a view in a .xib file

Our goal now is to load this nib file, manually, in code, when the app runs. Edit ViewController.swift and, in the viewDidLoad method body, insert this line of code:

NSBundle.mainBundle().loadNibNamed("View", owner: nil, options: nil)

Build and run the app. Hey, what happened? Where’s the designed view from View.xib? Did our nib fail to load?

No. Our nib did not fail to load. We loaded it! But we’ve omitted two further steps. Remember, there are three tasks you have to perform when you load a nib:

1. Load the nib.

2. Obtain the instances that it creates as it loads.

3. Do something with those instances.

We performed the first task — we loaded the nib — but we didn’t obtain any instances from it. Thus, those instances were created and then vanished in a puff of smoke! In order to prevent that, we need to capture those instances somehow. The call to loadNibNamed:owner:options:returns an array of the top-level nib objects instantiated from the loading of the nib. Those are the instances we need to capture! We have only one top-level nib object — the UIView — so it is sufficient to capture the first (and only) element of this array. Rewrite our code to look like this:

let arr = NSBundle.mainBundle().loadNibNamed("View", owner: nil, options: nil)

let v = arr[0] as! UIView

We have now performed the second task: we’ve captured the instances that we created by loading the nib. The variable v now refers to a brand-new UIView instance. But still nothing seems to happen when we build and run the app, because we aren’t doing anything with that UIView. That’s the third task. Let’s fix that by doing something clear and dramatic with the UIView: we’ll put it into our interface! Rewrite our code once again:

let arr = NSBundle.mainBundle().loadNibNamed("View", owner: nil, options: nil)

let v = arr[0] as! UIView

self.view.addSubview(v)

Build and run the app. There’s our view! This proves that our loading of the nib worked: we can see, in our running app’s interface, the view that we designed in the nib (Figure 7-8).

A nib-loaded view appears in our interface

Figure 7-8. A nib-loaded view appears in our interface

Connections

A connection is something in a nib file. It unites two nib objects, running from one to the other. The connection has a direction: that’s why I use the words “from” and “to” to describe it. I’ll call the two objects the source and the destination of the connection.

There are two kinds of connection: outlet connections and action connections. The rest of this section describes them, explains how to create and configure them, and explains the nature of the problems that they are intended to solve.

Outlets

When a nib loads and its instances come into existence, there’s a problem: those instances are useless unless you can get a reference to them. In the preceding section, we solved that problem by capturing the array of top-level objects instantiated by the loading of the nib. But there’s another way: use an outlet. This approach is more complicated — it requires some advance configuration, which can easily go wrong. But it is also more common, especially when nibs are loaded automatically.

An outlet is a connection that has a name, which is effectively just a string. When the nib loads, something unbelievably clever happens. The source object and the destination object are no longer just potential objects in a nib; they are now real, full-fledged instances. The outlet’s name is now immediately used to locate an instance property of the same name in the outlet’s source object, and the destination object is assigned to that property.

For example, suppose there’s a Dog object and a Person object in a nib, and suppose a Dog has a master instance property. Then if we make an outlet from the Dog object to the Person object in the nib, and if that outlet is named "master", then when the nib loads and the Dog instance and the Person instance are created, that Person instance will be assigned as the value of that Dog instance’s master property (Figure 7-9).

How an outlet provides a reference to a nib-instantiated object

Figure 7-9. How an outlet provides a reference to a nib-instantiated object

WARNING

The nib-loading mechanism can’t magically create an instance property — that is, it doesn’t cause the source object, once instantiated, to have an instance property of the correct name if it didn’t have one before. The class of the source object has to have been defined with this instance property already. Thus, for an outlet to work, preparation must be performed in two different places: in the class of the source object, and in the nib. This is a bit tricky; Xcode does try to help you get it right, but it is still possible to mess it up. (I will discuss ways of messing it up, in detail, later in this chapter.)

The Nib Owner

To use an outlet to capture a reference to an instance created from a nib, we need an outlet that runs from an object outside the nib to an object inside the nib. This seems metaphysically impossible — but it isn’t. The nib editor permits such an outlet to be created, using the nib owner object. First, I’ll tell you where to find the nib owner object in the nib editor; then I’ll explain what it is:

§ In a storyboard scene, the nib owner is the top-level view controller. It is the first object listed for that scene in the document outline, and the first object shown in the scene dock.

§ In a .xib file, the nib owner is a proxy object. It is the first object shown in the document outline or dock, and is listed under Placeholders as the File’s Owner.

The nib owner object in the nib editor represents an instance that already exists outside the nib at the time that the nib is loaded. When the nib is loaded, the nib-loading mechanism doesn’t instantiate this object; it is already an instance. Instead, the nib-loading mechanism substitutes the real, already existing instance for the nib owner object, using it to fulfill any connections that involve the nib owner.

But wait! How does the nib-loading mechanism know which real, already existing instance to substitute for the nib owner object in the nib? It knows because it is told, in one of two ways, at nib-loading time:

§ If your code loads the nib either by calling loadNibNamed:owner:options: or by calling instantiateWithOwner:options:, you specify an owner object as the owner: argument.

§ If a view controller instance loads a nib automatically in order to obtain its main view, the view controller instance specifies itself as the owner object.

For example, return to our Dog object and Person object. Suppose there is a Person nib object in our nib, but no Dog nib object. Instead, the nib owner object is a Dog. A Dog has a master instance property. We configure an outlet in the nib from the Dog nib owner object to the Person object — an outlet called "master". We then load the nib with an existing Dog instance as owner. The nib-loading mechanism will match the Dog nib owner object with this already existing actual Dog instance, and will set the newly instantiated Person instance as that Dog instance’s master(Figure 7-10).

An outlet from the nib owner object

Figure 7-10. An outlet from the nib owner object

Return now to Empty View, and let’s reconfigure things to demonstrate this mechanism. We’re already loading the View nib in code in ViewController.swift. This code is running inside a ViewController instance. So we’ll use that instance as the nib owner. This will be a little tedious to configure, but bear with me, because understanding how to use this mechanism is crucial. Here we go:

1. First, we need an instance property in ViewController. At the start of the body of the ViewController class declaration, insert the property declaration, like this:

2. class ViewController: UIViewController {

@IBOutlet var v : UIView! = nil

The var declaration you already understand; we’re making an instance property called v. It is declared as an Optional because it won’t have a “real” value when the ViewController instance is created; it’s going to get that value through the loading of the nib, later. The @IBOutletattribute is a hint to Xcode to allow us to create the outlet in the nib editor.

3. Edit View.xib. Our first step must be to ensure that the nib owner object is designated as a ViewController instance. Select the File’s Owner proxy object and switch to the Identity inspector. In the first text field, under Custom Class, set the Name value as ViewController. Tab out of the text field and save.

4. Now we’re ready to make the outlet! In the document outline, hold down the Control key and drag from the File’s Owner object to the View; a little line follows the mouse as you drag. Release the mouse. A little HUD (heads-up display) appears, listing possible outlets we are allowed to create (Figure 7-11). There are two of them: view and v. Click v (not view!).

5. Finally, we need to modify our nib-loading code. We no longer need to capture the top-level array of instantiated objects. That’s the whole point of this exercise! Instead, we’re going to load the nib with ourself as owner. This will cause our v instance property to be set automatically, so we can proceed to use it immediately:

6. NSBundle.mainBundle().loadNibNamed("View", owner: self, options: nil)

self.view.addSubview(self.v)

Creating an outlet

Figure 7-11. Creating an outlet

Build and run. It works! The first line loaded the nib and set our v instance property to the view instantiated from the nib. Thus, the second line can display self.v in the interface, because self.v now is that view.

Let’s sum up what we just did. Our preparatory configuration was a little tricky, because it was performed in two places — in code, and in the nib:

§ In code, there must be an instance property in the class whose instance will act as owner when the nib loads. (Not only did we create the property, but we also marked it as @IBOutlet.)

§ In the nib editor, the class of the nib owner object must be set to the class whose instance will act as owner when the nib loads.

§ In the nib editor, an outlet must be created, with the same name as the property, from the nib owner to some nib object. (This will be possible only if the other two configurations were correctly performed.)

If all those things are true, then, when the nib loads, if it is loaded with an owner of the correct class, that owner’s instance property will be set to the outlet destination.

Automatically Configured Nibs

In some situations, the configuration of the owner class and the nib is performed for us. Now that we’ve gone through the work of configuring the owner and the nib manually, we can understand and appreciate those automatic configurations.

An important example is how a view controller gets its main view. A view controller has a view property. The actual view will typically come from a nib. So the view controller instance needs to act as owner when that nib loads, and there needs to be a view outlet from the nib owner object to that view. If you examine an actual nib that holds a view controller’s main view, you’ll see that this is, in fact, the case.

Look at our Empty Window project. Edit Main.storyboard. It has one scene, whose nib owner object is the View Controller object. Select the View Controller in the document outline. Switch to the Identity inspector. It tells us that the nib owner object’s class is indeed ViewController!

Now, still with the View Controller in the document outline selected, switch to the Connections inspector. It tells us that there is indeed an outlet connection from the View Controller to the View object, and that this is outlet is indeed named "view"! If you hover the mouse over that outlet connection, the View object in the canvas is highlighted, to help you identify it.

That explains completely how this view controller gets its main view! The storyboard scene associates this view controller instance with the view nib containing this view. When the view controller needs its main view (because that view is about to be displayed in the interface), the view nib loads — with the view controller as owner. Thus, the view controller’s view property is set to the view that we design here. The view is then displayed in the interface — and that’s why the design that you construct here actually appears in the running app.

The same sort of thing is true in our Truly Empty project from Chapter 6. Edit MyViewController.xib. The nib owner object is the File’s Owner proxy object. Select the File’s Owner object. Switch to the Identity inspector. It tells us that the nib owner object’s class is MyViewController! Switch to the Connections inspector. It tells us that there is an outlet connection to the View object, called "view"!

That explains how this view controller gets its main view. We told this view controller where to find its nib when we instantiated it by saying MyViewController(nibName:"MyViewController", bundle:nil). But the nib itself was already correctly configured, because Xcode set it up that way when we created the MyViewController class and checked the “Also create XIB file” checkbox. The view controller loads the nib with itself as owner, and the outlet works: the view from the nib file becomes the view controller’s view, and appears in the interface.

Misconfigured Outlets

Setting up an outlet to work correctly involves several things being true at the same time. I guarantee that at some point in the future you will fail to get this right, and your outlet won’t work properly. Don’t be offended, and don’t be afraid; be prepared! This happens to everyone. The important thing is to recognize the symptoms so that you know what’s gone wrong. We’re deliberately going to make things go wrong, so that we can explore the main ways that an outlet can be incorrectly configured:

Outlet name doesn’t match a property name in the source class

Start with our working Empty Window example. Run the project to prove that all is well. Now, in ViewController.swift, change the property name to vv:

@IBOutlet var vv : UIView! = nil

In order to get the code to compile, you’ll also have to change the reference to this property in viewDidLoad:

self.view.addSubview(self.vv)

The code compiles just fine. But when you run it, the app crashes with this message in the console: “This class is not key value coding-compliant for the key v.”

That message is just a technical way of saying that the name of the outlet in the nib (which is still v) doesn’t match the name of a property of the nib’s owner when the nib loads — because we changed the name of that property to vv and thus wrecked the configuration. In effect, we had everything set up correctly, but then we went behind the nib editor’s back and removed the corresponding instance property from the outlet source’s class. When the nib loads, the runtime can’t match the outlet’s name with any property in the outlet’s source — the ViewController instance — and we crash.

There are other ways to bring about this same misconfiguration. For example, you could change things so that the nib owner is an instance of the wrong class:

NSBundle.mainBundle().loadNibNamed("View", owner: nil, options: nil)

We made the owner argument nil, so a generic NSObject instance is supplied. The effect is the same: the NSObject class has no property with the same name as the outlet, so the app crashes when the nib loads, complaining about the owner not being “key value coding-compliant.”

No outlet in the nib

Fix the problem from the previous example by changing both references to the property name from vv back to v in ViewController.swift. Run the project to prove that all is well. Now we’re going to mess things up at the other end! Edit View.xib. Select the File’s Owner and switch to the Connections inspector, and disconnect the v outlet by clicking the X at the left end of the second cartouche. Run the project. We crash with this error message in the console: “Fatal error: unexpectedly found nil while unwrapping an Optional value.”

We removed the outlet from the nib. So when the nib loaded, our ViewController instance property v, which is typed as an implicitly unwrapped Optional wrapping a UIView (UIView!), was never set to anything. Thus, it kept its initial value, which is nil. We then tried to use the implicitly unwrapped Optional by putting it into the interface:

self.view.addSubview(self.v)

Swift tries to obey by unwrapping the Optional for real, but you can’t unwrap nil, so we crash.

No view outlet

For this one, you’ll have to use the Truly Empty example from Chapter 6, where we load a view controller’s main view from a .xib file; I can’t demonstrate the problem using a .storyboard file, because the storyboard editor guards against it. In the Truly Empty project, edit theMyViewController.xib file. Select the File’s Owner object, switch to the Connections inspector, and disconnect the view outlet. Run the project. We crash at launch time: “Loaded the ‘MyViewController’ nib but the view outlet was not set.”

The console message says it all. A nib that is to serve as the source of a view controller’s main view must have a connected view outlet from the view controller (the nib owner object) to the view.

Deleting an Outlet

Deleting an outlet coherently — that is, without causing one of the problems described in the previous section — involves working in several places at once, just as creating an outlet does. I recommend proceeding in this order:

1. Disconnect the outlet in the nib.

2. Remove the outlet declaration from the code.

3. Attempt compilation and let the compiler catch any remaining issues for you.

Let’s suppose, for example, that you decide to delete the v outlet from the Empty Window project. You would follow the same three-step procedure that I just outlined:

1. Disconnect the outlet in the nib. To do so, edit View.xib, select the source object (the File’s Owner proxy object), and disconnect the v outlet in the Connections inspector by clicking the X.

2. Remove the outlet declaration from the code. To do so, edit ViewController.swift and delete or comment out the @IBOutlet declaration line.

3. Remove other references to the property. The easiest way is to attempt to build the project; the compiler issues an error on the line referring to self.v in ViewController.swift, because there is now no such property. Delete or comment out that line, and build again to prove that all is well.

More Ways to Create Outlets

Earlier, we created an outlet by first declaring an instance property in a class file, and then, in the nib editor, by control-dragging from the source (an instance of that class) to the destination in the document outline and choosing the desired outlet property from the HUD (heads-up display). Xcode provides many other ways to create outlets — too many to list here. I’ll survey some of the most interesting.

We’ll continue to use the Empty Window project and the View.xib file. Keep in mind that all of this works exactly the same way for a .storyboard file.

To prepare, delete the outlet in View.xib using the Connections inspector (if you haven’t already done so). In ViewController.swift, create (or uncomment) the property declaration, and save:

@IBOutlet var v : UIView! = nil

Drag from source Connections inspector

You can drag from a circle in the Connections inspector in the nib editor to connect the outlet. In View.xib, select the File’s Owner and switch to the Connections inspector. The v outlet is listed here, but it isn’t connected: the circle at its right is open. Drag from the circle next to v to the UIView object in the nib. You can drag to the view in the canvas or in the document outline. You don’t need to hold the Control key as you drag from the circle, and there’s no HUD because you’re dragging from a specific outlet, so Xcode knows which one you mean.

Drag from destination Connections inspector

Now let’s make that same move the other way round. Delete the outlet in the nib. Select the View and look at the Connections inspector. We want an outlet that has this view as its destination: that’s a “referencing outlet.” Drag from the circle next to New Referencing Outlet to the File’s Owner object. The HUD appears: click v to make the outlet connection.

Drag from source HUD

You can summon a HUD that effectively is the same as the Connections inspector. Let’s start with that HUD. Again delete the outlet in the Connections inspector. Control-click the File’s Owner. A HUD appears, looking a lot like the Connections inspector! Drag from the circle at the right of v to the UIView.

Drag from destination HUD

Again, let’s make that same move the other way round. Delete the outlet in the Connections inspector. Either in the canvas or in the document outline, Control-click the view. There’s the HUD showing its Connections inspector. Drag from the New Referencing Outlet circle to the File’s Owner. A second HUD appears, listing possible outlets; click v.

Again, delete the outlet. Now we’re going to create the outlet by dragging between the code and the nib editor. This will require that you work in two places at once: you’re going to need an assistant pane. In the main editor pane, show ViewController.swift. In the assistant pane, showView.xib, in such a way that the view is visible.

Drag from property declaration to nib

Next to the property declaration in the code, in the gutter, is an empty circle. What do you think it’s for? Drag from it right across the barrier to the View in the nib editor (Figure 7-12). The outlet connection is formed in the nib; you can see this by looking at the Connections inspector, and also because, back in the code, the circle in the gutter is now filled in. Hover over the filled circle, or click it, to learn what the outlet in the nib is connected to. You can click the little menu that appears when you click in the filled circle to navigate to the destination object.

Connecting an outlet by dragging from code to nib editor

Figure 7-12. Connecting an outlet by dragging from code to nib editor

Here’s one more way — the most amazing of all. Keep the two-pane arrangement from the preceding example. Again, delete the outlet (you will probably need to use the Connections inspector or HUD in the nib editor pane to do this). Also delete the @IBOutlet line from the code! We’re going to create the property declaration and connect the outlet, all in a single move!

Drag from nib to code

Control-drag from the view in the nib editor across the pane boundary to just inside the body of the class ViewController declaration. A HUD offers to Insert Outlet or Outlet Collection (Figure 7-13). Release the mouse. A popover appears, where you can configure the declaration to be inserted into your code. Configure it as shown in Figure 7-14: you want an outlet, and this property should be named v. Click Connect. The property declaration is inserted into your code, and the outlet is connected in the nib, in a single move.

Creating an outlet by dragging from nib editor to code

Figure 7-13. Creating an outlet by dragging from nib editor to code

Configuring a property declaration

Figure 7-14. Configuring a property declaration

WARNING

Making an outlet by connecting directly between code and the nib editor is extremely cool and convenient, but don’t be fooled: there’s no such direct connection. There are always, if an outlet is to work properly, two distinct and separate things — an instance property in a class, and an outlet in the nib, with the same name and coming from an instance of that class. It is the identity of the names and classes that allows the two to be matched at runtime when the nib loads, so that the instance property is properly set at that moment. Xcode tries to help you get everything set up correctly, but it is not in fact magically connecting the code to the nib.

Outlet Collections

An outlet collection is an array instance property (in code) matched (in a nib) by multiple connections to objects of the same type.

For example, suppose a class contains this property declaration:

@IBOutlet var arr: [UIView]!

The outcome is that, in the nib editor, using an instance of this class as a source object, you find that the Connections inspector lists arr — not under Outlets, but under Outlet Collections. This means that you can form multiple arr outlets, each one connected to a different UIView object in the nib. When the nib loads, those UIView instances become the elements of the array arr; the order in which the outlets are formed is the order of the elements in the array.

The advantage of this arrangement is that your code can refer to multiple interface objects instantiated from the nib by number (the index into the array) instead of your having to devise and manipulate a separate name for each one. This turns out to be particularly useful when forming outlets to such things as autolayout constraints and gesture recognizers.

Action Connections

In addition to outlet connections in a nib, there are also action connections. An action connection, like an outlet connection, is a way of giving one object in a nib a reference to another. It’s not a property reference; it’s a message-sending reference.

An action is a message emitted automatically by a Cocoa UIControl interface object (a control), and sent to another object, when the user does something to it, such as tapping the control. The various user behaviors that will cause a control to emit an action message are called events. To see a list of possible events, look at the UIControl class documentation, under “Control Events.” For example, in the case of a UIButton, the user tapping the button corresponds to the UIControlEvents.TouchUpInside event.

For this architecture to work, the control object must know three things:

§ What control event to respond to

§ What message to send (method to call) when that control event occurs (the action)

§ What object to send that message to (the target)

An action connection in a nib builds the knowledge of those three things into itself. It has the control object as its source; its destination is the target; and you tell the action connection, as you form it, what the control event and action message should be. To form the action connection, you need first to configure the class of the destination object so that it has a method suitable as an action message.

To experiment with action connections, we’ll need a UIControl object in a nib, such as a button. You may already have such a button in the Empty Window project’s Main.storyboard file. However, it’s probable that, when the app runs, we’ve been covering the button with the view that we’re loading from View.xib. So first clear out the ViewController class declaration body in ViewController.swift, so that there is no outlet property and no manual nib-loading code; this should be all that’s left:

class ViewController: UIViewController {

}

Now let’s arrange to use the view controller in our Empty Window project as a target for an action message emitted by the button’s UIControlEvents.TouchUpInside event (meaning that the button was tapped). We’ll need a method in the view controller that will be called by the button when the button is tapped. To make this method dramatic and obvious, we’ll have the view controller put up an alert window. Insert this method into the ViewController.swift declaration body:

class ViewController: UIViewController {

@IBAction func buttonPressed(sender:AnyObject) {

let alert = UIAlertController(

title: "Howdy!", message: "You tapped me!", preferredStyle: .Alert)

alert.addAction(

UIAlertAction(title: "OK", style: .Cancel, handler: nil))

self.presentViewController(alert, animated: true, completion: nil)

}

}

The @IBAction attribute is like @IBOutlet: it’s a hint to Xcode itself, asking Xcode to make this method available in the nib editor. And indeed, if we look in the nib editor, we find that it is now available: edit Main.storyboard, select the View Controller object and switch to the Connections inspector, and you’ll find that buttonPressed: is now listed under Received Actions.

In Main.storyboard, in the single scene that it contains, the top-level View Controller’s View should contain a button. (We created it earlier in this chapter: see Figure 7-5.) If it doesn’t, add one, and position it in the upper left corner of the view. Our goal now is to connect that button’s Touch Up Inside event, as an action, to the buttonPressed: method in ViewController.

As with an outlet connection, there is a source and a destination. The source here is the button; the destination is the representative of ViewController in the storyboard — View Controller, the owner of the nib containing the button. There are many ways to form this outlet connection, all of them completely parallel to the formation of an action connection. The difference is that we must configure both ends of the connection. At the button (source) end, we must specify that the control event we want to use is Touch Up Inside; fortunately, this is the default for a UIButton, so we might be able to skip this step. At the view controller (destination) end, we must specify that the action method to be called is buttonPressed:.

For example, let’s form the action connection by simply Control-dragging from the button to the view controller in the nib editor:

1. Control-drag from the button (in the canvas or in the document outline) to the View Controller listing in the document outline (or to the view controller icon in the scene dock above the view in the canvas).

2. A HUD listing possible connections appears (Figure 7-15); it lists mostly segues, but it also lists Sent Events, and buttonPressed: in particular.

3. Click the buttonPressed: listing in the HUD.

A HUD showing an action method

Figure 7-15. A HUD showing an action method

The action connection has now been formed. This means that when the app runs, any time the button gets a Touch Up Inside event — meaning that it was tapped — it will send the action message buttonPressed: to the target, which is the view controller instance. We know what that method should do: it should put up an alert. Try it! Build and run the app, and when the app appears in the Simulator, tap the button. It works!

More Ways to Create Actions

Other ways in which you can form the action connection in the nib, having created the action method in ViewController.swift, include the following:

§ Control-click the view controller. A HUD appears, similar to the Connections inspector. Drag from buttonPressed: (under Received Actions) to the button. Another HUD appears, listing possible control events. Click Touch Up Inside.

§ Select the button and use the Connections inspector. Drag from the Touch Up Inside circle to the view controller. A HUD appears, listing the known action methods in the view controller; click buttonPressed:.

§ Control-click the button. A HUD appears, similar to the Connections inspector. Proceed as in the previous case.

§ Arrange to see ViewController.swift in one pane and the storyboard in the other. The buttonPressed: method in ViewController.swift has a circle to its left, in the gutter. Drag from that circle across the pane boundary to the button in the nib.

As with an outlet connection, the most impressive way to make an action connection is to drag from the nib editor to your code, inserting the action method and forming the action connection in the nib in a single move. To try this, first delete the buttonPressed: method in your code and delete the action connection in the nib. Arrange to see ViewController.swift in one pane and the storyboard in the other. Now:

1. Control-drag from the button in the nib editor to an empty area in the ViewController class declaration’s body. A HUD offering to create an outlet or an action appears in the code. Release the mouse.

2. The popover view appears. This is the tricky part. By default, the popover view is offering to create an outlet connection. That isn’t what you want; you want an action connection! Change the Connection pop-up menu to Action. Now you can enter the name of the action method (buttonPressed) and configure the rest of the declaration (the defaults are probably good enough: see Figure 7-16).

Configuring an action method declaration

Figure 7-16. Configuring an action method declaration

Xcode forms the action connection in the nib, and inserts a stub method into your code:

@IBAction func buttonPressed(sender: AnyObject) {

}

The method is just a stub (Xcode can’t read your mind and guess what you want the method to do), so in real life, at this point, you’d insert some functionality between those curly braces. As with an outlet connection, the filled circle next to the code in an action method tells you that Xcode believes that this connection is correctly configured, and you can click the filled circle to learn, and navigate to, the object at the source of the connection.

Misconfigured Actions

As with an outlet connection, an action configuration involves setting things up correctly at both ends (the nib and the code) so that they match. Thus, you can wreck an action connection’s configuration and crash your app. The typical misconfiguration is that the name of the action method as embedded in the action connection in the nib no longer matches the name of the action method in the code.

To see this, change the name of the function in the code from buttonPressed to something else, like buttonPushed. Now run the app and tap the button. Your app crashes, displaying in the console the dreaded error message, “Unrecognized selector sent to instance.” A selector is a message — the name of a method. The runtime tried to send a message to an object, but that object turned out to have no corresponding method (because we renamed it). If you look a little earlier in the error message, it even tells you the name of this method:

-[Empty_Window.ViewController buttonPressed:]

The runtime is telling you that it tried to call the buttonPressed: method in your Empty Window module’s ViewController class, but the ViewController class has no buttonPressed: method.

Connections Between Nibs — Not!

You cannot draw an outlet connection or an action connection between an object in one nib and an object in another nib. For example:

§ You can’t open nib editors on two different .xib files and Control-drag a connection from one to the other.

§ In a .storyboard file, you cannot Control-drag a connection between an object in one scene and an object in another scene.

If you expect to be able to do this, you haven’t understood what a nib is (or what a scene is, or what a connection is).

The reason is simple: objects in a nib together will become instances together, at the moment when the nib loads, so it makes sense to connect them in the nib, because we know what instances we’ll be talking about when the nib loads. The two objects may both be instantiated from the nib, or one of them may be a proxy object (the nib owner), but they must both be represented in the same nib, so that the actual instances can be configured in relation to one another on a particular occasion when this nib loads.

If an outlet connection or an action connection were drawn from an object in one nib to an object in another nib, there would be no way to understand what actual future instances the connection is supposed to connect, because they are different nibs and will be loaded at different times (if ever). The problem of communicating between an instance generated from one nib and an instance generated from another nib is a special case of the more general problem of how to communicate between instances in a program, discussed in Chapter 13.

Additional Configuration of Nib-Based Instances

By the time a nib finishes loading, its instances are fully fledged; they have been initialized and configured with all the attributes dictated through the Attributes and Size inspectors, and their outlets have been used to set the values of the corresponding instance properties. Nevertheless, you might want to append your own code to the initialization process as an object is instantiated from a loading nib. This section describes some ways you can do that.

A common situation is that a view controller, functioning as the owner when a nib containing its main view loads (and therefore represented in the nib by the nib owner object), has an outlet to an interface object instantiated from the nib. In this architecture, the view controller can perform further configuration on that interface object, because it has a reference to it after the nib loads — the corresponding instance property. The earliest place where it can perform such configuration is its viewDidLoad method. At the time viewDidLoad is called, the view controller’s view has loaded — that is, the view controller’s view property has been set to its actual main view, instantiated from the nib — and all outlets have been connected; but the view is not yet in the visible interface.

Another possibility is that you’d like the nib object to configure itself, over and above whatever configuration has been performed in the nib. Often, this will be because you’ve got a custom subclass of a built-in interface object class — in fact, you might want to create a custom class, just so you have a place to put this self-configuring code. The problem you’re trying to solve might be that the nib editor doesn’t let you perform the configuration you’re after, or that you have many objects that need to be configured in some identical, elaborate way, so that it makes more sense for them to configure themselves by virtue of sharing a common class than to configure each one individually in the nib editor.

One approach is to implement awakeFromNib in your custom class. The awakeFromNib message is sent to all nib-instantiated objects just after they are instantiated by the loading of the nib: the object has been initialized and configured and its connections are operational.

For example, let’s make a button whose background color is always red, regardless of how it’s configured in the nib. (This is a nutty example, but it’s dramatically effective.) In the Empty Window project, we’ll create a button subclass, RedButton:

1. In the Project navigator, choose File → New → File. Specify iOS → Source → Cocoa Touch Class. Click Next.

2. Call the new class RedButton. Make it a subclass of UIButton. Click Next.

3. Make sure you’re saving into the project folder, with the Empty Window group, and make sure the Empty Window app target is checked. Click Create. Xcode creates RedButton.swift.

4. In RedButton.swift, inside the body of the RedButton class declaration, implement awakeFromNib:

5. override func awakeFromNib() {

6. super.awakeFromNib()

7. self.backgroundColor = UIColor.redColor()

}

We now have a UIButton subclass that turns itself red when it’s instantiated from a nib. But we have no instance of this subclass in any nib. Let’s fix that. Edit the storyboard, select the button that’s already in the main view, and use the Identity inspector to change this button’s class to RedButton.

Now build and run the project. Sure enough, the button is red!

A further possibility is to take advantage of the User Defined Runtime Attributes in the nib object’s Identity inspector. This can allow you to configure, in the nib editor, aspects of a nib object for which the nib editor itself provides no built-in interface. What you’re actually doing here is sending the nib object, at nib-loading time, a setValue:forKeyPath: message; key paths are discussed in Chapter 10. Naturally, the object needs to be prepared to respond to the given key path, or your app will crash when the nib loads.

For example, one of the disadvantages of the nib editor is that it provides no way to configure layer attributes. Let’s say we’d like to use the nib editor to round the corners of our red button. In code, we would do that by setting the button’s layer.cornerRadius property. The nib editor gives no access to this property. Instead, we can select the button in the nib editor and use the User Defined Runtime Attributes in the Identity inspector. We set the Key Path to layer.cornerRadius, the Type to Number, and the Value to whatever value we want — let’s say 10 (Figure 7-17). Now build and run; sure enough, the button’s corners are now rounded.

Rounding a button’s corners with a runtime attribute

Figure 7-17. Rounding a button’s corners with a runtime attribute

New in Xcode 6, you can configure a custom property of a nib object by making that property inspectable. To do so, add the @IBInspectable attribute to the property’s declaration in your code. This causes the property to be listed in the nib object’s Attributes inspector.

For example, let’s make it possible to configure our button’s border in the nib editor. At the start of the RedButton class declaration body, add this code:

@IBInspectable var borderWidth : CGFloat {

get {

return self.layer.borderWidth

}

set {

self.layer.borderWidth = newValue

}

}

That code declares a RedButton property, borderWidth, and makes it a façade in front of the layer’s borderWidth property. It also causes the nib editor to display that property in the Attributes inspector for any button that is an instance of the RedButton class (Figure 7-18). The result is that when we give this property a value in the nib editor, that value is sent to the setter for this property at nib-loading time, and the button border appears with that width.

An inspectable property in the nib editor

Figure 7-18. An inspectable property in the nib editor

To intervene with a nib object’s initialization even earlier, if the object is a UIView (or a UIView subclass), you can implement init(coder):. Note that, for a UIView, init(frame:) is not called during instantiation by the loading of a nib — init(coder:) is called instead. (Implementing init(frame:), and then wondering why your code isn’t called when the view is instantiated from a nib, is a common beginner mistake.) A minimal implementation would look like this:

required init(coder aDecoder: NSCoder) {

super.init(coder:aDecoder)

// your code here

}