Using View Controllers

In this chapter, you are going to write an application with two view controllers. One will display the HypnosisView you created in the last chapter, and the other will let the user get the current time by tapping a button (Figure 7.4). We will swap in the views using a UITabBarController.

Figure 7.4  HypnoTime screens

HypnoTime screens

In Xcode, create a new Window-based Application iPhone project named HypnoTime. (Yes, there is a Tab Bar Application project template, but using that template makes things seem more complicated and magical than they are. Do not use it for this application.)

You will re-use HypnosisView in this application. Use Finder to locate HypnosisView.h and HypnosisView.m and drag them into the project navigator of this project. When the sheet appears, check the box labeled Copy items into destination group’s folder and click Finish. Also, add the icons Hypno.png and Time.png (available at http://www.bignerdranch.com/solutions/iOSProgramming.zip) to the project navigator in the same way.

Creating the UITabBarController

In HypnoTimeAppDelegate.m, create the tab bar controller and set it as the rootViewController of the window:

-​ ​(​B​O​O​L​)​a​p​p​l​i​c​a​t​i​o​n​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
 ​ ​ ​ ​d​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​W​i​t​h​O​p​t​i​o​n​s​:​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​l​a​u​n​c​h​O​p​t​i​o​n​s​
{​
 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​U​I​T​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​*​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​=​ ​[​[​U​I​T​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​S​e​t​ ​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​a​s​ ​r​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​o​f​ ​w​i​n​d​o​w​
 ​ ​ ​ ​[​[​s​e​l​f​ ​w​i​n​d​o​w​]​ ​s​e​t​R​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​]​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​w​i​n​d​o​w​ ​r​e​t​a​i​n​s​ ​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​,​ ​w​e​ ​c​a​n​ ​r​e​l​e​a​s​e​ ​o​u​r​ ​r​e​f​e​r​e​n​c​e​
 ​ ​ ​ ​[​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​r​e​l​e​a​s​e​]​;​


 ​ ​ ​ ​/​/​ ​S​h​o​w​ ​t​h​e​ ​w​i​n​d​o​w​
 ​ ​ ​ ​[​[​s​e​l​f​ ​w​i​n​d​o​w​]​ ​m​a​k​e​K​e​y​A​n​d​V​i​s​i​b​l​e​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

Build and run the application. The big white space is where your views will get swapped in. Also notice the black tab bar at the bottom of the window. This is the UITabBar, a subview of the UITabBarController’s view. Right now, there aren’t any tab bar items in it, but we’ll fix that in the next section.

In previous applications, you manipulated the view hierarchy directly. For example, in Chapter 5, you added subviews to the window dragging them onto the window in a XIB file. In Chapter 6, you added subviews to the window using the method addSubview:.

When using view controllers, you don’t have to manipulate the view hierarchy directly. UIWindow implements a method named setRootViewController:. Passing an instance of UIViewController as the argument to this method automatically adds the view of that view controller as a subview of the window and resizes it to fit. The window also retains its root view controller.

Figure 7.5  View hierarchy with UITabBarController

View hierarchy with UITabBarController

Creating view controllers and tab bar items

To create the first view controller for HypnoTime, select New File... from the New menu item in the File menu. While there is a UIViewController subclass template option, you won’t use it here. Choose Objective-C class and select NSObject as the superclass on the next pane. Name this class CurrentTimeViewController.

Open CurrentTimeViewController.h and change the superclass to UIViewController.

@​i​n​t​e​r​f​a​c​e​ ​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

Now create another class in the same way, but name it HypnosisViewController. In HypnosisViewController.h, change the superclass to UIViewController.

@​i​n​t​e​r​f​a​c​e​ ​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

Now you need to create instances of the view controllers and add them to the tab bar controller. At the top of HypnoTimeAppDelegate.m, import the header files for these classes.

#​i​m​p​o​r​t​ ​"​H​y​p​n​o​T​i​m​e​A​p​p​D​e​l​e​g​a​t​e​.​h​"​
#​i​m​p​o​r​t​ ​"​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​
#​i​m​p​o​r​t​ ​"​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

Then, also in HypnoTimeAppDelegate.m, modify the application:​didFinishLaunchingWithOptions: method to create instances of these view controllers and add them to the tab bar controller.

-​ ​(​B​O​O​L​)​a​p​p​l​i​c​a​t​i​o​n​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
 ​ ​ ​ ​d​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​W​i​t​h​O​p​t​i​o​n​s​:​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​l​a​u​n​c​h​O​p​t​i​o​n​s​
{​
 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​U​I​T​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​*​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​=​ ​[​[​U​I​T​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​w​o​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​s​
 ​ ​ ​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​v​c​1​ ​=​ ​[​[​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​v​c​2​ ​=​ ​[​[​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​n​ ​a​r​r​a​y​ ​c​o​n​t​a​i​n​i​n​g​ ​t​h​e​ ​t​w​o​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​s​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​v​i​e​w​C​o​n​t​r​o​l​l​e​r​s​ ​=​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​v​c​1​,​ ​v​c​2​,​ ​n​i​l​]​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​v​i​e​w​C​o​n​t​r​o​l​l​e​r​s​ ​a​r​r​a​y​ ​r​e​t​a​i​n​s​ ​v​c​1​ ​a​n​d​ ​v​c​2​,​ ​w​e​ ​c​a​n​ ​r​e​l​e​a​s​e​
 ​ ​ ​ ​/​/​ ​o​u​r​ ​o​w​n​e​r​s​h​i​p​ ​o​f​ ​t​h​e​m​ ​i​n​ ​t​h​i​s​ ​m​e​t​h​o​d​
 ​ ​ ​ ​[​v​c​1​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​v​c​2​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​A​t​t​a​c​h​ ​t​h​e​m​ ​t​o​ ​t​h​e​ ​t​a​b​ ​b​a​r​ ​c​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​[​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​s​:​v​i​e​w​C​o​n​t​r​o​l​l​e​r​s​]​;​

 ​ ​ ​ ​/​/​ ​P​u​t​ ​t​h​e​ ​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​'​s​ ​v​i​e​w​ ​o​n​ ​t​h​e​ ​w​i​n​d​o​w​
 ​ ​ ​ ​[​[​s​e​l​f​ ​w​i​n​d​o​w​]​ ​s​e​t​R​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​]​;​
 ​ ​ ​ ​[​t​a​b​B​a​r​C​o​n​t​r​o​l​l​e​r​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​S​h​o​w​ ​t​h​e​ ​w​i​n​d​o​w​
 ​ ​ ​ ​[​[​s​e​l​f​ ​w​i​n​d​o​w​]​ ​m​a​k​e​K​e​y​A​n​d​V​i​s​i​b​l​e​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

Build and run the application. The tab bar now has two tabs that you can select, but there isn’t anything interesting about the tabs. Every view controller has a tab bar item that controls the text or icon that appears in the tab bar as shown in Figure 7.6.

Figure 7.6  UITabBarItem example

UITabBarItem example

Let’s start by putting a title on the tab bar items. Open HypnosisViewController.m. Create a new init method, override the designated initializer for the superclass (UIViewController), and edit the viewDidLoad method to match the code below:

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​/​/​ ​C​a​l​l​ ​t​h​e​ ​s​u​p​e​r​c​l​a​s​s​'​s​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​n​d​l​e​:​n​i​l​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​t​a​b​ ​b​a​r​ ​i​t​e​m​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​b​B​a​r​I​t​e​m​ ​*​t​b​i​ ​=​ ​[​s​e​l​f​ ​t​a​b​B​a​r​I​t​e​m​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​i​v​e​ ​i​t​ ​a​ ​l​a​b​e​l​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​T​i​t​l​e​:​@​"​H​y​p​n​o​s​i​s​"​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​i​d​)​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​i​b​N​a​m​e​ ​b​u​n​d​l​e​:​(​N​S​B​u​n​d​l​e​ ​*​)​b​u​n​d​l​e​
{​
 ​ ​ ​ ​/​/​ ​D​i​s​r​e​g​a​r​d​ ​p​a​r​a​m​e​t​e​r​s​ ​-​ ​n​i​b​ ​n​a​m​e​ ​i​s​ ​a​n​ ​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​d​e​t​a​i​l​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​i​n​i​t​]​;​
}​

/​/​ ​T​h​i​s​ ​m​e​t​h​o​d​ ​g​e​t​s​ ​c​a​l​l​e​d​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​w​h​e​n​ ​t​h​e​ ​v​i​e​w​ ​i​s​ ​c​r​e​a​t​e​d​
-​ ​(​v​o​i​d​)​v​i​e​w​D​i​d​L​o​a​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​D​i​d​L​o​a​d​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​L​o​a​d​e​d​ ​t​h​e​ ​v​i​e​w​ ​f​o​r​ ​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​"​)​;​

 ​ ​ ​ ​/​/​ ​S​e​t​ ​t​h​e​ ​b​a​c​k​g​r​o​u​n​d​ ​c​o​l​o​r​ ​o​f​ ​t​h​e​ ​v​i​e​w​ ​s​o​ ​w​e​ ​c​a​n​ ​s​e​e​ ​i​t​
 ​ ​ ​ ​[​[​s​e​l​f​ ​v​i​e​w​]​ ​s​e​t​B​a​c​k​g​r​o​u​n​d​C​o​l​o​r​:​[​U​I​C​o​l​o​r​ ​o​r​a​n​g​e​C​o​l​o​r​]​]​;​
}​

Open CurrentTimeViewController.m and do all the same things, but use a different background color for the view:

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​/​/​ ​C​a​l​l​ ​t​h​e​ ​s​u​p​e​r​c​l​a​s​s​'​s​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​n​d​l​e​:​n​i​l​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​t​a​b​ ​b​a​r​ ​i​t​e​m​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​b​B​a​r​I​t​e​m​ ​*​t​b​i​ ​=​ ​[​s​e​l​f​ ​t​a​b​B​a​r​I​t​e​m​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​i​v​e​ ​i​t​ ​a​ ​l​a​b​e​l​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​T​i​t​l​e​:​@​"​T​i​m​e​"​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​i​d​)​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​i​b​N​a​m​e​ ​b​u​n​d​l​e​:​(​N​S​B​u​n​d​l​e​ ​*​)​b​u​n​d​l​e​
{​
 ​ ​ ​ ​/​/​ ​D​i​s​r​e​g​a​r​d​ ​p​a​r​a​m​e​t​e​r​s​ ​-​ ​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​d​e​t​a​i​l​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​i​n​i​t​]​;​
}​

-​ ​(​v​o​i​d​)​v​i​e​w​D​i​d​L​o​a​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​D​i​d​L​o​a​d​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​L​o​a​d​e​d​ ​t​h​e​ ​v​i​e​w​ ​f​o​r​ ​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​"​)​;​

 ​ ​ ​ ​/​/​ ​S​e​t​ ​t​h​e​ ​b​a​c​k​g​r​o​u​n​d​ ​c​o​l​o​r​ ​o​f​ ​t​h​e​ ​v​i​e​w​ ​s​o​ ​w​e​ ​c​a​n​ ​s​e​e​ ​i​t​
 ​ ​ ​ ​[​[​s​e​l​f​ ​v​i​e​w​]​ ​s​e​t​B​a​c​k​g​r​o​u​n​d​C​o​l​o​r​:​[​U​I​C​o​l​o​r​ ​g​r​e​e​n​C​o​l​o​r​]​]​;​
}​

Build and run the application. Two labeled tab bar items will appear on the tab bar (Figure 7.7). Tap one and then the other, and you will see that the views for the view controllers are getting swapped in.

Figure 7.7  Tab bar items with labels

Tab bar items with labels

(If you are wondering why we made a new designated initializer for the UIViewController subclasses, hang on until Chapter 10 – we’ll explain it then.)

Now let’s add icons. Open HypnosisViewController.m and edit the init method:

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​n​d​l​e​:​n​i​l​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​b​B​a​r​I​t​e​m​ ​*​t​b​i​ ​=​ ​[​s​e​l​f​ ​t​a​b​B​a​r​I​t​e​m​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​T​i​t​l​e​:​@​"​H​y​p​n​o​s​i​s​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​U​I​I​m​a​g​e​ ​f​r​o​m​ ​a​ ​f​i​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​i​ ​=​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​N​a​m​e​d​:​@​"​H​y​p​n​o​.​p​n​g​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​P​u​t​ ​t​h​a​t​ ​i​m​a​g​e​ ​o​n​ ​t​h​e​ ​t​a​b​ ​b​a​r​ ​i​t​e​m​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​I​m​a​g​e​:​i​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Next, open CurrentTimeViewController.m and edit its init method:

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​n​d​l​e​:​n​i​l​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​b​B​a​r​I​t​e​m​ ​*​t​b​i​ ​=​ ​[​s​e​l​f​ ​t​a​b​B​a​r​I​t​e​m​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​T​i​t​l​e​:​@​"​T​i​m​e​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​i​ ​=​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​N​a​m​e​d​:​@​"​T​i​m​e​.​p​n​g​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​I​m​a​g​e​:​i​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Now when you build and run the application, you will also see icons in the tab bar (Figure 7.8).

Figure 7.8  Tab bar items with labels and icons

Tab bar items with labels and icons

Creating views for the view controllers

Now that you have a perfectly nice tab bar with two view controllers (and the two corresponding tab bar items), it’s time to give your view controllers views. (Technically, they already have views, but they are default, blank views.) There are two ways to do this:

  • create the view programmatically
  • create a XIB file

How do you know when to do one versus the other? Here’s a good rule-of-thumb: if the view has no subviews, create it programmatically; if it has subviews, create a XIB file.

When the view needs to be created, the view controller is sent the message loadView. In HypnosisViewController, you are going to override this method so that it creates an instance of HypnosisView programmatically. When an instance of a UIViewController is instantiated, its view is not created right away. A UIViewController’s view is created when it is placed in a view hierarchy (also known as the first time it appears on screen Add the following import statement and method to HypnosisViewController.m:

#​i​m​p​o​r​t​ ​"​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​
#​i​m​p​o​r​t​ ​"​H​y​p​n​o​s​i​s​V​i​e​w​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​H​y​p​n​o​s​i​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

-​ ​(​v​o​i​d​)​l​o​a​d​V​i​e​w​
{​
 ​ ​ ​ ​H​y​p​n​o​s​i​s​V​i​e​w​ ​*​h​v​ ​=​ ​[​[​H​y​p​n​o​s​i​s​V​i​e​w​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​C​G​R​e​c​t​Z​e​r​o​]​;​
 ​ ​ ​ ​[​h​v​ ​s​e​t​B​a​c​k​g​r​o​u​n​d​C​o​l​o​r​:​[​U​I​C​o​l​o​r​ ​w​h​i​t​e​C​o​l​o​r​]​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​V​i​e​w​:​h​v​]​;​
 ​ ​ ​ ​[​h​v​ ​r​e​l​e​a​s​e​]​;​
}​

We no longer want the background of the view to be orange, so delete the following line from the viewDidLoad method in HypnosisViewController.m:

[​[​s​e​l​f​ ​v​i​e​w​]​ ​s​e​t​B​a​c​k​g​r​o​u​n​d​C​o​l​o​r​:​[​U​I​C​o​l​o​r​ ​o​r​a​n​g​e​C​o​l​o​r​]​]​;​

Also delete the similar line of code from the viewDidLoad method in CurrentTimeViewController.m.

[​[​s​e​l​f​ ​v​i​e​w​]​ ​s​e​t​B​a​c​k​g​r​o​u​n​d​C​o​l​o​r​:​[​U​I​C​o​l​o​r​ ​g​r​e​e​n​C​o​l​o​r​]​]​;​

Build and run the application. You should see a HypnosisView like the one in Figure 7.9.

Figure 7.9  HypnosisViewController

HypnosisViewController

The CurrentTimeViewController’s view will have subviews (a UIButton and a UILabel). Therefore, you will use a XIB file to load its view. Create a new XIB file by selecting New File... from the New item in the File menu. From the iOS section, choose User Interface. Then, select the Empty XIB template. On the next pane, select iPhone from the pop-up menu.

Figure 7.10  Creating an empty XIB

Creating an empty XIB

Name this file CurrentTimeViewController.xib and save it. Then, select it in the project navigator to show it in Xcode.

Demystifying the XIB: File's Owner

This is an empty XIB file, there are only two objects in it: File's Owner and First Responder. The goal of this XIB file is to create the view for the CurrentTimeViewController.

Open the utilities area and drag a UIView onto the canvas. Notice that this view was added to the gutter on the left edge of the XIB editor pane.

We would like this view to be the view of the CurrentTimeViewController. In other words, the instance variable view of CurrentTimeViewController should point at the view. Therefore, we must have an outlet connection from an instance of CurrentTimeViewController to the view. In previous exercises, this was simple: there was a AppDelegate object, and we’d set up connections between the view objects in the XIB file and that AppDelegate. However, there is not a CurrentTimeViewController object in this XIB file that you can make connections to and from.

Your first intuition may be to add a CurrentTimeViewController to the XIB file. But, if there was a CurrentTimeViewController object in the XIB file, whenever that XIB file was loaded it would create an instance of CurrentTimeViewController. This application already has an instance of CurrentTimeViewController, the one you created in application:​didFinishLaunchingWithOptions:. If you were to add an instance of this class to the XIB file, you would have two CurrentTimeViewController instances.

Figure 7.11  File's Owner

File's Owner

Instead, we need some way to connect objects loaded from the XIB file to objects that exist in memory before the XIB file is loaded. This way, the already existing CurrentTimeViewController could set its view instance variable to point at the view loaded from this XIB file. This is where the File's Owner comes in. The File's Owner is a placeholder object. When a XIB file is loaded, placeholder objects are not instantiated. You can think of a placeholder object as a hole in which existing objects can be placed so that connections can be made between them and the objects in the XIB file.

Take a peek at the available connections for the File's Owner by Control-clicking on it. There aren’t any. That is because the type of the File's Owner defaults to NSObject, which has no IBOutlets. Because a CurrentTimeViewController will be the placeholder object, we must change the type of the File's Owner in this XIB file to be that class.

Select the File's Owner placeholder object on the outline view and click the File's Owner icon to show the identity inspector. Change the Class field to CurrentTimeViewController and hit return.

Figure 7.12  Changing the type of an object in a XIB file

Changing the type of an object in a XIB file

Now, Control-click on the File's Owner to see the available outlets. There is now a view outlet available because UIViewController’s view is an IBOutlet and CurrentTimeViewController is a subclass of UIViewController. Connect this outlet to the view in this XIB file.

Select the view and open the attributes inspector. Change its background color to something obnoxious, like bright purple.

Figure 7.13  Changing the background color of a view

Changing the background color of a view

Build and run the application. While running, select the Time tab. Notice that you are looking at the exact same view you created in CurrentTimeViewController.xib.

So, how does this work? When you create an instance of a UIViewController subclass, you pass it the name of a XIB file through its designated initializer, initWithNibName:bundle:. When the view controller is asked for its view, it checks to see if a XIB file with that name exists in your application bundle. (If you specify nil as the name, it will search for a XIB file whose name matches the name of the view controller subclass.) It then loads that XIB file. You can be more explicit about the XIB file loaded in CurrentTimeViewController.m.

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​@​"​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​n​d​l​e​:​n​i​l​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​b​B​a​r​I​t​e​m​ ​*​t​b​i​ ​=​ ​[​s​e​l​f​ ​t​a​b​B​a​r​I​t​e​m​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​T​i​t​l​e​:​@​"​T​i​m​e​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​i​ ​=​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​N​a​m​e​d​:​@​"​T​i​m​e​.​p​n​g​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​b​i​ ​s​e​t​I​m​a​g​e​:​i​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

The loadView method is responsible for this XIB loading behavior. The default implementation of this method does the checking for and loading of a XIB file. Therefore, when a view controller is loading its view from a XIB file, you do not override loadView. If instead you want to create a view for a view controller programmatically, you must override loadView so it does not load a XIB file.

The instance of CurrentTimeViewController that is loading the XIB file places itself in the File's Owner hole of that XIB file. Any connections to or from the File's Owner are made on that instance of CurrentTimeViewController. This concept allows placeholder objects to get pointers to objects loaded from a XIB file. If you did not set pointers from objects in memory to objects loaded from a XIB file, the loaded objects would be alive but nothing would have a pointer to it. This would be a leak.

Take a look at MainWindow.xib and check out the File's Owner in the identity inspector: its class is UIApplication. When your application first launches, an instance of UIApplication is created and it loads MainWindow.xib. The UIApplication is the File's Owner. The File's Owner of this file has a outlet connection for its delegate that has been connected to HypnoTimeAppDelegate. Therefore, after the XIB file loads, the delegate of the UIApplication is set to point at the HypnoTimeAppDelegate object – the reason why HypnoTimeAppDelegate gets sent the message application:​didFinishLaunchingWithOptions:.

Understanding the File's Owner is an integral part of demystifying the magic of a XIB file. In Chapter 15, we will talk about how objects are archived into the XIB file.

Now that your XIB file is set up to work properly, you can continue creating the interface for CurrentTimeViewController. In CurrentTimeViewController.h, add an outlet and an action.

#​i​m​p​o​r​t​ ​<​U​I​K​i​t​/​U​I​K​i​t​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​C​u​r​r​e​n​t​T​i​m​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
{​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​L​a​b​e​l​ ​*​t​i​m​e​L​a​b​e​l​;​
}​
-​ ​(​I​B​A​c​t​i​o​n​)​s​h​o​w​C​u​r​r​e​n​t​T​i​m​e​:​(​i​d​)​s​e​n​d​e​r​;​

@​e​n​d​

Save this file.

In CurrentTimeViewController.xib, drag a UILabel and UIButton object onto the view that is already there. Configure these objects and make connections, as shown in Figure 7.14.

Figure 7.14  Button and Label

Button and Label

Implement the action method in CurrentTimeViewController.m:

-​ ​(​I​B​A​c​t​i​o​n​)​s​h​o​w​C​u​r​r​e​n​t​T​i​m​e​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​N​S​D​a​t​e​ ​*​n​o​w​ ​=​ ​[​N​S​D​a​t​e​ ​d​a​t​e​]​;​

 ​ ​ ​ ​/​/​ ​S​t​a​t​i​c​ ​h​e​r​e​ ​m​e​a​n​s​ ​"​o​n​l​y​ ​o​n​c​e​.​"​ ​T​h​e​ ​*​v​a​r​i​a​b​l​e​*​ ​f​o​r​m​a​t​t​e​r​
 ​ ​ ​ ​/​/​ ​i​s​ ​c​r​e​a​t​e​d​ ​w​h​e​n​ ​t​h​e​ ​p​r​o​g​r​a​m​ ​i​s​ ​f​i​r​s​t​ ​l​o​a​d​e​d​ ​i​n​t​o​ ​m​e​m​o​r​y​.​
 ​ ​ ​ ​/​/​ ​T​h​e​ ​f​i​r​s​t​ ​t​i​m​e​ ​t​h​i​s​ ​m​e​t​h​o​d​ ​r​u​n​s​,​ ​f​o​r​m​a​t​t​e​r​ ​w​i​l​l​
 ​ ​ ​ ​/​/​ ​b​e​ ​n​i​l​ ​a​n​d​ ​t​h​e​ ​i​f​-​b​l​o​c​k​ ​w​i​l​l​ ​e​x​e​c​u​t​e​,​ ​c​r​e​a​t​i​n​g​
 ​ ​ ​ ​/​/​ ​a​n​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​o​b​j​e​c​t​ ​t​h​a​t​ ​f​o​r​m​a​t​t​e​r​ ​w​i​l​l​ ​p​o​i​n​t​ ​t​o​.​
 ​ ​ ​ ​/​/​ ​S​u​b​s​e​q​u​e​n​t​ ​e​n​t​r​y​ ​i​n​t​o​ ​t​h​i​s​ ​m​e​t​h​o​d​ ​w​i​l​l​ ​r​e​u​s​e​ ​t​h​e​ ​s​a​m​e​
 ​ ​ ​ ​/​/​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​o​b​j​e​c​t​.​

 ​ ​ ​ ​s​t​a​t​i​c​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​*​f​o​r​m​a​t​t​e​r​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​i​f​ ​(​!​f​o​r​m​a​t​t​e​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​m​a​t​t​e​r​ ​=​ ​[​[​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​f​o​r​m​a​t​t​e​r​ ​s​e​t​T​i​m​e​S​t​y​l​e​:​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​S​h​o​r​t​S​t​y​l​e​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​[​t​i​m​e​L​a​b​e​l​ ​s​e​t​T​e​x​t​:​[​f​o​r​m​a​t​t​e​r​ ​s​t​r​i​n​g​F​r​o​m​D​a​t​e​:​n​o​w​]​]​;​
}​

Build and run the application. You will be able to switch back and forth between the two views. Clicking the button on the time view will display the current time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.54.136