UIKit is the framework that you will find yourself use most often. It defines the core components of an iOS application, from labels and buttons to table views and navigation controllers. In this article, not only will we start our exploration of the UIKit framework, we will also explore the internals of an iOS project and the basic building blocks of iOS applications.
What is the UIKit framework?
While the Foundation framework defines classes, protocols, and functions for both iOS and OS X development, the UIKit framework is exclusively geared towards iOS development. It is the equivalent of the Application Kit or AppKit framework for OS X development.
Like Foundation, UIKit defines classes, protocols, functions, data types, and constants. It also adds additional functionality to various Foundation classes, such as NSObject
, NSString
, and NSValue
through the use of Objective-C categories.
Objective-C categories are a convenient way to add extra methods to existing classes without the need for subclassing. Read this tutorial by Aaron Crabtree if you want to learn more about Objective-C categories.
Instead of exploring the key classes of UIKit as we did for the Foundation framework, we will create and journey through a new iOS project and explore the classes we encounter. By taking this approach, it will quickly become clear in what context a class is used and how each class fits in the broader scheme of an iOS application and what role it plays.
A New Beginning
Launch Xcode and create a new project (File > New > Project…). In the iOS section on the left, select the Application category. From the list of project templates, choose the Single View Application template. This project template contains that basic building blocks of an iOS application and is therefore a good place to start our journey.
Name the project UIKit and enter an organization name and company identifier. Enter a class prefix, as I explained earlier in this series, set Devices to iPhone, and enable Automatic Reference Counting (ARC). For this project, we will not be using Storyboards or Unit Tests.
Tell Xcode where you want to save the new project and make sure to put the project under version control. Revisit this article for more information about version control and its benefits.
Files and Folders
We have learned quite a few new things since the last time we created an iOS project from scratch so it might be a good idea to explore the various files and folders of our new project to see if they are familiar to you.
In the Project Navigator, you should see three folders at the root of the project (figure 5), (1) Products, (2) Frameworks, and (3) a folder with your project’s name. Let’s take a look at the contents of each of these folders.
Products
The Products folder currently contains one item. The item bears the name of our project and has an extension of .app. The Products folder contains the application – or applications – created by the project after compilation of the source code.
Have you noticed that the name of the product is highlighted in red? Whenever Xcode is unable to locate a file, it highlights the file in red. Because the project hasn’t been compiled yet, Xcode hasn’t created the product yet.
Frameworks
The Frameworks folder contains the frameworks your project is linked against. In the previous article, our project was only linked against the Foundation framework. This project, however, is based on an iOS application template and therefore linked against three frameworks, (1) Foundation, (2) UIKit, and (3) Core Graphics. You might remember the Core Graphics framework from earlier in this series. The framework contains the interfaces for the (Quartz) 2D drawing API.
There is one other location in our project that specifies which frameworks the project is linked against. Select your project in the Project Navigator, choose the only item in the Targets section on the left, and open the Build Phases tab at the top. By opening the Link Binary with Libraries drawer, you are presented with the same list of frameworks that we saw in the Frameworks folder (figure 6). Linking our project against another iOS system framework is as easy as clicking the plus button at the bottom of the list and selecting a framework from the list.
Project Folder
Most of your time is spent in the project folder, which currently contains five files and one folder named Supporting Files. Let’s start by taking a look at the contents of the folder.
- <PROJECT>-Info.plist: This file, commonly referred to as the “info-dot-plist” file of a project, is a property list that contains various configuration settings. Most of these settings can also be modified by selecting the project in the Project Navigator, choosing the target in the Targets list, and opening the Summary tab.
- InfoPlist.strings: If Info.plist contains values that need to be localized, then you can do so by specifying them in InfoPlist.strings. Application localization is something we won’t cover in this series.
- main.m: This file should be familiar by now. When creating iOS applications, however, you will rarely modify the contents of main.m. It contains the application’s
main
function, which is where all the magic starts. Even though we won’t be modifying main.m, it is critical to your application. - <PROJECT>-Prefix.pch: This is the project’s precompiled header file, which we already encountered earlier in this series. As its name implies, the header files listed in the precompiled header file are precompiled by Xcode, which brings down the time it takes to compile your project. The Foundation and UIKit frameworks don’t change very often so there is no need to compile them every time you compile your project. In addition to precompiling the header files listed in the precompiled header file, each source file in your project is prefixed with the listed header files.
- Launch Images: The final three files in the Supporting Files folder are the launch images for the project. Launch images are the images that the operating system displays while your application is loading. Why does our project contain three launch images? The image named Default.png is used for iPhone and iPod touch devices that don’t have a retina screen, whereas Default@2x.png targets devices with a retina screen. The third launch image, Default-568@2x.png, targets iPhone and iPod Touch devices with a 4″ screen. Note that our project currently only targets the iPhone device family. If we were to create a Universal application, targeting both the iPhone and iPad device families, our project would need to include five launch images.
Application Components
Now that we know what the different files and folders in our project are for, it is time to explore the different components of an iOS application. As we continue our journey, we will come across several classes that belong to UIKit. Each class that belongs to the UIKit framework starts with the class prefix UI. Remember that Foundation classes have NS as class prefix.
The Model-View-Controller Pattern
Before we start exploring the UIKit framework, I want to talk about the Model-View-Controller (MVC) pattern. The MVC pattern is one of the most widespread patterns in object oriented programming. It promotes code reusability and has close ties with the concept of separation of responsibilities or concerns. One of the main objectives of the MVC pattern is the separation of an application’s business logic from its representation.
Cocoa Touch embraces the MVC pattern and it is therefore important to understand what it is and how it works. In other words, by understanding the MVC pattern, it will be much easier to grasp how the different components of an iOS application work together. In the MVC pattern, a model is in control of the business logic of an application. Interacting with a database, for example, is the responsibility of the model. The view presents the data that is managed by the model to the user. The view manages the user interface and user input.
The controller is the glue between the model and the view. While the model and the view don’t know about each other’s existence, the controller knows about both the model and the view. Because the controller knows about both model and view, it is often the least reusable piece of an application. The less an object knows about its environment and the objects it interacts with, the higher its reusability will be in future projects.
UIApplication
Even though the UIApplication
class is a key component of every iOS application, you won’t interact with it very often and you will rarely, if ever, have the need to subclass UIApplication
. When an application launches, a singleton of this class is created. Do you remember what a singleton object is? It means that only one object instance of the UIApplication
class is created during an application’s life cycle.
The UIApplication
instance is the entry point for user interaction and it dispatches events to the appropriate target objects. The exact meaning of this will become clear in a few minutes when we take a look at view controllers.
In most iOS applications, the UIApplication
instance has a delegate object associated with it. Whenever you create an iOS project using one of the provided templates, Xcode will create an application delegate class for you. Take a look at the names of the source files in the project folder in the Project Navigator. The first two files are named MTAppDelegate. The instance of this class is the delegate of the UIApplication
singleton. Before taking a closer look at the MTAppDelegate
class, we need to understand what the delegate pattern is.
Ole Begemann wrote an excellent article about the launch sequence of a typical iOS application. In this article, he talks about the various components involved and their role during the application launch. I highly recommend reading this article if you want to get a better understanding of the role of the UIApplication
class as well as the mysterious main()
function in main.m.
The Delegate Pattern
The delegate pattern is used extensively in Cocoa and Cocoa Touch. In the next article of this series, in which we explore the ins and outs of a table view, you will find out that table views rely heavily on the delegate (and data source) pattern. Like the MVC pattern, delegation is common in object oriented programming. The delegate pattern in Cocoa Touch, however, is slightly different, because it makes use of a delegate protocol to define the behavior of the delegate object.
Let’s jump ahead and take a look at table views. If you have spent any amount of time with an iPhone or iPad, then the UITableView
class should be familiar to you. It presents an ordered list of data to the user and it does this job very well. How does a table view know what to do when a row is tapped? Is this behavior included in the UITableView
class? Certainly not, because this behavior varies from application to application. If we were to include this behavior in the UITableView
class, it would not be very reusable.
The table view outsources this responsibility to a delegate object. Put differently, it delegates this task to another object, a delegate object. Take a moment to inspect the class reference of the UITableView
class. It has two instance variables named dataSource and delegate. The delegate object is notified by the table view whenever a user taps a row. It is the responsibility of the delegate object to respond to the user’s tap.
The data source of the table view is similar in terms of behavior. The main difference is that the table view asks the data source object for something -the data that it needs to display.
Delegate and data source objects, such as the table view’s delegate
and dataSource
objects, are almost always custom classes, classes that you create, because their implementation is application specific.
Before we take a look at the MTAppDelegate
class, it is important to understand that a delegate object conforms to a delegate protocol. In the previous article, we already learned about protocols in Objective-C and how they define behavior by defining a list of methods for its adopters to implement.
The Application Delegate
Now that we know what delegation is, it is time to explore the MTAppDelegate
class that Xcode created for us. At application launch, the application creates an instance of the MTAppDelegate
class. You typically never need to explicitly instantiate an application delegate object. Open the header file of the MTAppDelegate
class (MTAppDelegate.h) to examine its contents.
If we ignore the comments at the top, the first line imports the header files of the UIKit framework so we can work with its classes and protocols.
#import <UIKit/UIKit.h>
The second line is a forward class declaration. A forward class declaration is nothing more than a message to the compiler to tell it that the MTViewController
class exists. You could consider it as a promise to the compiler. If you were to remove the forward class declaration, the compiler will throw an error when you try to compile the project.
@class MTViewController;
The next line should be familiar. This is the start of the interface of the MTAppDelegate
class as denoted by the @interface
directive. It specifies the name of the class and the class’s superclass, UIResponder
. Between angle brackets are the protocols the class conforms to. The MTAppDelegate
class conforms to one protocol, the UIApplicationDelegate
protocol.
@interface MTAppDelegate : UIResponder <UIApplicationDelegate>
The next two lines are property declarations, which should look familiar. The first property, window
, is an instance of UIWindow
(a subclass of UIView
) and the second property, viewController
, is an instance of MTViewController
. The words between parentheses, strong and nonatomic, are optional property attributes that specify the storage semantics and behavior of the property. You can ignore them for now.
@property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) MTViewController *viewController;
The most interesting part of the interface of the MTAppDelegate
class is the UIApplicationDelegate
protocol. Take a look at the reference of the UIApplicationDelegate
protocol for a complete list of the methods the protocol defines.
The protocol defines dozens of methods and I encourage you to explore a few of them to get an idea of the protocol’s capabilities. The method that is most interesting to us at this point is application:didFinishLaunchingWithOptions:
. When the UIApplication
instance has finished preparing the application for launch, it will notify the delegate, our MTAppDelegate
instance, that the application will finish launching – but that it hasn’t yet. Why does it notify the application delegate of this event? The UIApplication
instance notifies its delegate about this event so that it has an opportunity to prepare for application launch. Head over to the implementation file of MTAppDelegate
to see what I mean. The first method in the implementation file is application:didFinishLaunchingWithOptions:
and it should more or less look like the one pasted below.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.viewController = [[MTViewController alloc] initWithNibName:@"MTViewController" bundle:nil]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; }
The application:didFinishLaunchingWithOptions:
method provides a reference to the UIApplication
instance and a dictionary with options, which are of no interest to us at this point. In the first line of the method implementation, an instance of UIWindow
is created and assigned to the window
property of the application delegate. Every iOS application has exactly one window. In this window, all the content of the application is displayed.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
On the third line (ignore the comment), the second property of the application delegate is set, an instance of the MTViewController
class. This is the other class that Xcode generated for us when creating the project. The view controller instance is created by sending an alloc
message to the class followed by initWithNibName:bundle:
. The nib name specified is identical to the file named MTViewController.xib in the Project Navigator. I will talk more about nib files in a few moments.
self.viewController = [[MTViewController alloc] initWithNibName:@"MTViewController" bundle:nil];
Next, the rootViewController
property of the window
property is set to the viewController
property. What? Take a long look at that line of code and read the previous sentence again. A window instance doesn’t display anything by default. It is nothing more than a blank canvas (so to speak). We need to provide it with some initial content in the form of a rootViewController
. I will talk more about view controllers in a few moments.
self.window.rootViewController = self.viewController;
You can understand the last two lines just by reading them. We send the window a message of makeKeyAndVisible
to tell the window to prepare itself to become visible (to the user). In the last line, we return YES
, because the method expects us to return a boolean – YES
or NO
value.
How do we know what the window is going to display and how can we control this? We have set the rootViewController
property of the window instance to the application delegate’s rootViewController
property. We need to learn more about view controllers to understand the rest of the story.
UIViewController
If you open MTViewController.h, you will notice that the MTViewController
class is a subclass of UIViewController
. Like MTAppDelegate
, the UIViewController
class is a subclass of UIResponder
. View controllers, or subclasses thereof, fall in the controller category of the MVC pattern. As the name implies, they control a view, an instance of the UIView
class, which falls in the view category of the MVC pattern.
The view of the view controller is usually created by invoking the view controller’s initWithNibName:bundle:
method, which was invoked by the application delegate in the application:didFinishLaunchingWithOptions:
method. The initWithNibName:bundle:
method loads the file named MTViewController.xib. Despite its .xib extension, MTViewController.xib is a nib file.
A nib file is a file that stores a part of the user interface of the application. Instead of using Xcode’s code editor, a nib file is edited using Interface Builder, an application built into Xcode. Select MTViewController.xib to open the nib file (figure 7). At its core, a nib file is nothing more than an object graph (a group of objects that are linked to one another through direct or indirect references). The nib file is loaded when the view controller invokes initWithNibName:bundle:
.
When you open a nib file, you see (1) a sidebar with two sections, Placeholders and Objects, and (2) a workspace at the center. The workspace displays all the objects listed in the Objects section. At the moment, only one object is listed, an instance of UIView
. The placeholders are objects that are not part of the object graph of the nib file. Instead, they are references to objects that live outside of the nib file. The most important placeholder is File’s Owner. As the name implies, File’s Owner is the owner of the nib file, that is, the object that owns the object graph. Can you guess which object that is? In iOS development, File’s Owner often refers to a view controller and this is also true in our example. You can verify this by selecting the File’s Owner object and opening the Identify Inspector in the Inspector on the right (figure 8). The class of File’s Owner is MTViewController
as we expected.
A view controller manages a view – and the view’s subviews as we will see later. To do this, the view controller needs to know about (read: have a reference to) the view. Our view controller, the File’s Owner object, does have a reference to the view in the object’s section. You can verify this by opening the Connections Inspector in the Inspector on the right (figure 9). Don’t forget to select the File’s Owner object first.
In the Connections Inspector, you should see a section named Outlets. The term outlet is a fancy word for a property that Interface Builder knows about and the value of which can be set in Interface Builder. Hover with your mouse over the outlet named view and observe how the view in the workspace is highlighted. That is the connection between the view controller and the view.
UIView
Even though your application can have only one instance of UIWindow
, it can have many views. The UIView
class is an important component of the UIKit framework as many classes inherit from it, either directly or indirectly. With the view controller’s nib file still open, you should see the Object Library (figure 10) at the bottom of the Inspector. Drag a label and a button to the gray view in the workspace. It doesn’t matter where you position them in the view as long as they are in the gray view (figure 10).
You will have noticed that three new objects have been added to the Objects section on the left (figure 11). You can ignore the Constraints object for now. Both the label (UILabel
) and the button (UIButton
) inherit from UIView
. Did you notice that the Label and Button objects are slightly indented compared to the View object? This indicates that the Label and Button objects are subviews of the View object. A view can have one or more subviews that it manages.
As I already mentioned, the UIView
class is an important component of UIKit. A view manages a rectangular area or frame on the screen. It manages the contents of the area, the subviews, and any interactions with the view’s contents. The UIView
class is a subclass of UIResponder
. You will learn much more about views over the course of this series.
Outlets
Let’s take a look at an example to illustrates the relationship between the nib file, the view it contains, and the view controller. These three components are important and I want to make sure that you understand just how they work together.
Earlier in this article, you added a label and a button. How does the view controller know about these objects? At the moment, they don’t appear in the Connections Inspector, but we can change that by telling the view controller about them. Open the view controller’s header file (MTViewController.h) and add a property for the label and for the button (see below). By adding the IBOutlet
keyword to the property declaration, Interface Builder (the IB in IBOutlet) knows about these properties.
@property IBOutlet UILabel *myLabel; @property IBOutlet UIButton *myButton;
Head back to the view controller’s nib file (MTViewController.xib), select the File’s Owner object, and open the Connections Inspector. The new properties are now listed in the list of Outlets. However, the view controller hasn’t yet made the connection between the new properties and the objects in the nib file. This is easy to remedy. Drag from the empty circle on the left of the myLabel
outlet to the label in the workspace (figure 12). This will make that all-important connection so that the view controller knows about it. Do the same for the button.
Even though we can change the text of the label in Interface Builder, let’s do this in the view controller to illustrate that the view controller has access to the label and button in the nib file. Open the view controller’s implementation file (MTViewController.m) and look for the viewDidLoad
method. Modify the viewDidLoad
method to reflect the implementation below. The comments have been omitted for clarity.
- (void)viewDidLoad { [super viewDidLoad]; [self.myLabel setText:@"This is an instance of UILabel"]; }
We can send messages to the label property by asking the view controller, self
, for its myLabel
property. By sending the myLabel
property a message of setText:
and passing a string literal, we update the label’s text. Note that setText:
is an accessor (setter). Even though it is possible to use Objective-C’s dot notation (see below), it is better to use the more traditional syntax as it better shows you what is actually happening.
self.myLabel.text = @"This is an instance of UILabel";
Run your application in the iOS Simulator by clicking the Run button in the top left and notice that the label’s text is indeed updated.
Actions
We have seen a lot of new things in this article. I want to end this article by talking about actions. Just like outlets, actions are nothing more than methods that Interface Builder knows about. Let’s see how this works. Open the view controller’s header file (MTViewController.h) and add the following method declaration somewhere in the view controller’s interface block.
- (IBAction)changeColor:(id)sender;
Don’t be confused by the IBAction
keyword. IBAction
is identical to void
, which means that the method simply returns no value. By taking a closer look at the method declaration, we see that it takes one argument of type id
, a reference to an Objective-C object. As the argument name implies, the argument of the method or action is the object that sent the message to the view controller. I will explain this in more detail in just a bit.
Open the view controller’s nib file (MTViewController.xib) once more, select the File’s Owner object, and open the Connections Inspector. A new section has appeared in the Connections Inspector named Received Actions and the action we just added is listed in this section. Drag from the empty circle on the left of the action to the button in the workspace. A small window with a list of options should appear (figure 13). The list contains all the possible events that the button can respond to. The one that interests us is Touch Up Inside (figure 14). This event is triggered when a user touches the button and lifts her finger while still inside the button.
Build and run your application once again and tap the button. Did the application crash for you as well? How did this happen? When you tapped the button, it sent a message of changeColor:
to the view controller. Even though the view controller declares a changeColor:
method, it does not implement this method yet. Whenever a message is sent to an object that does not implement a corresponding method, an exception is raised and the application crashes if the exception isn’t caught. You can verify this by running the application once more, tapping the button, and inspecting the output in the console window.
To remedy this, we need to implement the changeColor:
method. Open the view controller’s implementation file (MTViewController.m) and add the following method implementation somewhere in the implementation block. The implementation of the changeColor:
method is identical to the one we used earlier in this series. However, I have added two extra NSLog
calls to its implementation to show you that the sender of the message is indeed the button that we added to the nib file in Interface Builder.
- (IBAction)changeColor:(id)sender { NSLog(@"Sender Class > %@", [sender class]); NSLog(@"Sender Superclass > %@", [sender superclass]); int r = arc4random() % 255; int g = arc4random() % 255; int b = arc4random() % 255; UIColor *color = [UIColor colorWithRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0]; [self.view setBackgroundColor:color]; }
The method itself is pretty simple. We generate three random integers between 0 and 255, pass these values to colorWithRed:green:blue:alpha:
to generate a random color, and update the background color of the view controller’s view with the randomly generated color. Note that self.view
references the view that the view controller manages and that we saw earlier in the nib file.
Build and run your application one more time, tap the button, and don’t forget to inspect the output in Xcode’s console window. You will notice that the sender is not an instance of UIButton
, but a subclass of UIButton
. The UIRoundedRectButton
is a private UIKit class. This is just an implementation detail that shouldn’t concern you at this time.
Conclusion
In this article, we have explored a few classes of the UIKit framework and we took a close look at the various components of an iOS application. We will explore and work with the UIKit framework in more detail in the rest of this series. If you don’t fully understand the various concepts and patterns, then I am sure that you will as the series progresses. Don’t hesitate to leave a comment if you have question about this article.