Views and view controllers are the fundamental items that make up the user interface of any app, and there is plenty of information about them in Apple’s documentation. In fact, there is so much information that learning how they work together can be somewhat overwhelming for the new iOS programmer. Happily, there are really only a few basic things you need to know to get started. In this first part of a two-part article, we’ll build a simple app to understand how the user interface (UI) actually makes it onto the screen. Let’s get started with Figure A, which shows a sample UI and how it’s represented in Cocoa Touch.
Figure A
The UIScreen object represents the physical iPhone or iPad screen. Other than to perhaps query the size (the so-called bounds) of the screen, you will typically not interact much with the UIScreen object.
The UIWindow object provides drawing support for the screen, and your app will usually have just one. Other than setting the UIWindow object’s initial root view controller (discussed below), you won’t interact with the UIWindow object very much either.
Views
UIView objects are where the real magic begins to happen. They are attached to the UIWindow object and draw their contents when directed to do so by the window. The visual interface of your app is essentially a set of UIView objects, which are themselves managed by UIViewController objects.
The most important things to understand about UIView objects, or just simply views, at this point are:
- A view represents a single item on the user interface. It could be a control such as a button or slider, an input text field, or perhaps an image. A view covers a specific area on the screen and can be configured to respond to touch events.
- Views can be nested inside other views, which lead to the notion of a view hierarchy. Views placed inside another view are referred to as subviews of that view. Subviews refer to their containing “parent” view as their superview. Subviews are drawn and positioned relative to their superview, so for example, if the superview moves to a new location, the subviews move with it. In Figure A, the UIButton and UILabel objects are subviews of the main UIView.
- Views are not the place where the bulk of your program logic resides. Views can detect when a user interacts with them, such as a tap on a button control, but as those interactions happen, views send messages to other objects in your app, usually a view controller, to handle the processing.
- Views underpin many other objects in iOS. For example, all of the user interface controls are subclasses of UIControl, which in turn is a subclass of UIView. The upshot is that all interface controls inherit the functionality and properties of views. This becomes important, for example, when you’re trying to figure out how to manipulate a property of a button. If you can’t find what you’re looking for in the UIButton section of the documentation, you just might find it under UIView.
UIViewController objects interact with UIViews to determine what’s displayed by the views, handle any interactions with the user, and perform the logic of your program.
That’s all the theory we’ll need for our example app, but it’s worth investing time learning the details in Apple’s View Programming Guide for iOS. We will now build an app from scratch to understand exactly how the first view makes it onto the screen.
Building the app
Open Xcode and from the File menu, select New, and then New Project. A new workspace window will appear, and you will be presented with several application templates to choose from.
On the left-hand side, select Application from the iOS section. Select Empty Application and click the Next button.
Figure B
On the next pane, enter ViewMe for the Product Name and com.myappcompany as the Company Identifier. (Feel free to substitute your own product name and company identifier).
Leave the Class Prefix as is. It should be blank – XYZ is just a placeholder hint.
We want only the box labeled Use Automatic Reference Counting checked, so make sure to uncheck the others.
Set Device Family to iPhone, and click Next.
Figure C
A new pane will appear asking you where you would like to save the project. XCode will create a ViewMe project folder inside the directory you select.
Once the project is created, Xcode will open it in a workspace window as shown in Figure D.
Figure D
At this point we have just an application delegate. Click on AppDelegate.m and find the code shown in Figure E.
Figure E
Here the app’s main window is being allocated and initialized. The call to [[UIScreen mainScreen] bounds] returns a structure that contains the size of the physical screen, resulting in the window being sized to fill the entire screen. Next, the window’s background color is set to white. The call to makeKeyAndVisible makes this the main window and displays it in front of any other windows (remember, iOS apps typically they have only one window, though technically they could have more). At this point building and running the app results in blank screen, as shown in Figure F.
Figure F
Now let’s add a View and View Controller. Select the File menu, select New, and then select File. Select Objective-C class from the Cocoa Touch templates (Figure G) and click Next.
Figure G
On the next screen (Figure H), name the new class MyViewController. Make sure “With XIB for user interface” is checked, and click Next.
Figure H
Click Create on the screen that follows and the result should look like Figure I. A header (.h), implementation (.m) and nib (.xib) file have been created to represent MyViewController. If MyViewController.xib is not already open, click on it in the Files & Groups panel to open up Interface Builder (IB).
Figure I
The first thing to note is that the UIView is created for us inside the .xib (pronounced “nib”) file. It will be loaded when the View Controller is created by our app. Note also the complaint in the console window about the need for a root view controller. We’ll handle that in just a bit.
At this point our xib file contains just a single UIView object. Right-clicking on File’s Owner will show the current outlet connections. Outlets are used to make connections between objects in the user interface and instance variables in your code as shown in Figure J. Note that an outlet named “view” has been automatically created and connected to the UIView object. Where did this come from?
Figure J
A “view” instance variable is declared in the base UIViewController class and is inherited by your MyViewController subclass. XCode knows about this variable and automatically made an outlet connection for you when you created MyViewController. Using outlets, MyViewController can interact with the interface controls, as we’ll see in Part 2 of this article.
Interface
Let’s build out the interface a bit before we finally tie the whole picture together. Click on View in the Objects list so that it becomes active in the Inspector pane on the right side of the workspace. Make sure the Attributes Inspector is selected and then change the background color of the view to blue. (Figure K)
Figure K
Now go to lower right-hand side of workspace and find the Library section. Select the Label object and drag it onto the view. Center it both horizontally and vertically. Notice the helper lines that appear when you have it perfectly centered. Next, select a Round Rect Button and drag it underneath the Label. (Figure L)
Figure L
You can resize both objects by grabbing and dragging the resize handles on the corners until it looks similar to Figure M.
Figure M
Now let’s go back to the code. Click on AppDelegate.m in the Files & Groups section to bring up the implementation file again and add the code shown below in bold type.
#import "AppDelegate.h"
#import "MyViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
MyViewController *myViewController = [[MyViewController alloc]initWithNibName:@"MyViewController" bundle:nil];
[self.window setRootViewController:myViewController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
When we create myViewController, we call the initWithNibName method to initialize it using the “MyViewController.xib” file (the .xib extension is assumed by the method), which you will remember contains our UIView object. The call to setRootViewController does two important things.
First, it establishes myViewController as the first view controller in the view controller hierarchy, which will satisfy the warning we saw earlier about root view controllers. Second, it adds myViewController’s view as the first subview of the window, which is the magic that causes our user interface to be displayed. If we now build and run the application, we see our shiny new interface. (Figure N)
Figure N
Understanding how your first view is wired to the main window is an important fundamental to grasp. As you begin developing more complicated apps and using templates that use objects like Navigation and Tab Controllers, understanding exactly what’s happening at this early stage of application startup may be key to figuring out what’s happening when your app won’t compile or behave as expected.
In the second part of this article, we’ll wire the label and buttons to the view controller to understand how view controllers actually interact with views.