Creating an Interactive iPhone App
Previous | Table of Contents | Next |
An Overview of the iPhone Application Development Architecture | Understanding iPhone Views, Windows and the View Hierarchy |
Learn SwiftUI and take your iOS Development to the Next Level |
In the previous chapter we looked at the design patterns that we will need to learn and use regularly in the course of developing iPhone applications. In this chapter we will work through a detailed example that will demonstrate the View-Controller relationship (we will introduce the Model part of the MVC equation in the next chapter) together with the implementation of Target-Action pattern to create an example interactive iPhone application.
Creating the New Project
The purpose of the application we are going to create is to perform unit conversions from Fahrenheit to Centigrade. Obviously the first step is to create a new Xcode project to contain our application. Start Xcode and on the Welcome screen select Create a new Xcode project. On the New Project screen choose the Application option located under iPhone OS in the left hand panel, select View-based Application and click Choose…. In the drop down file selection pane name the project UnitConverter and choose a suitable folder to hold the project files before clicking the Save button to proceed to the main Xcode project window. As outlined in previous chapters, Xcode has created a number of files for us to use in developing our application.
Creating the User Interface
Before we begin developing the logic for our interactive application we are going to start by designing the user interface. When we created the new project, Xcode generated an Interface Builder NIB file for us and named it UnitConverterViewController.xib. It is within this file that we will create our user interface, so double click on this file to open Interface Builder and load the file. Once Interface Builder has loaded, one or more windows will be visible:
Some of the windows in the above figure may not be displayed by default. To display the View window double click on the View icon in the UnitConverterViewController.xib window. To display the Library window select Tools -> Library and to display the View Attributes window select Tools -> Attributes Inspector.
From the Cocoa Touch – Inputs and Values section of the Library window drag a Text Field object onto the View. Resize the object and position it so that it appears as follows:
Within the Attribute Inspector window, type the words Enter temperature into the Placeholder text field. This text will then appear in a light gray color in the text field as a visual cue to the user.
Now that we have created the text field for the user to enter the temperature into the next step is to add a Button object that can be pressed to initiate the conversion. To achieve this drag and drop a Round Rect Button object from the Library to the View. Double click the button object so that it changes to text edit mode and type the word Convert onto the button. Finally, select the button and drag it beneath the text field until the blue dotted line appears indicating it is centered vertically in relation to the text field before releasing the mouse button:
The last user interface object we need to add is a label where the result of the conversion will be displayed. Add this by dragging a Label object from the Library window to the View and position it beneath the button. Stretch the width of the label so that it is approximately a third of the overall width of the view and reposition it using the blue guidelines to ensure it is centered in relation to the button.
<google>IOSBOX</google>
Double click on the label to highlight the text and press the backspace key to clear the text (we will set the text from within a method of our View Controller class when the conversion calculation has been performed). Though the label is now no longer visible it is still present in the view. If you click where it is located it will be highlighted with the resize dots.
At this point the user interface design phase of our project is complete and the view should appear as illustrated in the following figure. We now are ready to try out a test build and run.
Building and Running the Sample Application
Before we move on to writing the controller code for our application and then connecting it to the user interface we have designed we should first perform a test build and run of the application so far. From within Interface Builder select the File -> Save menu option and then exit from the builder via the Interface Builder -> Quit Interface Builder option. Back in the Xcode project window click on the Build and Run button located in the toolbar to compile the application and run it in the simulator. If you are not happy with the way your interface looks feel free to reload it into Interface Builder and make improvements. Assuming the user interface appears to your satisfaction in the simulator we are ready to start writing some Objective-C code to add some logic to our controller. Exit from the simulator before proceeding to the next step by selecting the iPhone Simulator -> Quit iPhone Simulator menu option.
Adding Actions and Outlets
When the user enters a temperature value into the text field and touches the convert button we need to trigger an action that will perform a calculation to convert the temperature. The result of that calculation will then be presented to the user on the label object. The Action will be in the form of a method that we will declare and implement in our View Controller class. Access to the text field and label objects from the view controller method will be implemented through the use of Outlets.
Before we begin, now is a good time to highlight an example of the use of subclassing as previously described in An Overview of the iPhone Application Development Architecture. The UIKit framework contains a class called UIViewController that provides the basic foundation for adding view controllers to an application. In order to create a functional application, however, we inevitably need to add functionality specific to our application to this generic view controller class. This is achieved by subclassing the UIViewController class and extending it with the additional functionality we need.
When we created our new project, Xcode anticipated our needs and automatically created a subclass of UIViewController and named it UnitConverterViewController (using as a prefix the name that we gave to our new project). In so doing, Xcode also created two source files; a header file named UnitConverterViewController.h and a source code file named UnitConverterViewController.m.
Selecting the UnitConverterViewController.h file in the main Xcode window will display the contents of the file in the editing window:
// // UnitConverterViewController.h // UnitConverter // // Created by Techotopia on 3/30/10. // Copyright __MyCompanyName__ 2010. All rights reserved. // #import <UIKit/UIKit.h> @interface UnitConverterViewController : UIViewController { } @end
As we can see from the above code, a new class called UnitConverterViewController has been created that is a subclass of the UIViewController class belonging to the UIKit framework.
The next step is to extend the subclass to include the two outlets and our action method. We will begin by adding outlets for our text field and label objects by declaring variables and using the IBOutlet keyword:
#import <UIKit/UIKit.h> @interface UnitConverterViewController : UIViewController { UITextField *tempText; UILabel *resultLabel; } @property (nonatomic, retain) IBOutlet UILabel *resultString; @property (nonatomic, retain) IBOutlet UITextField *tempString; @end
Next we need to declare the action that will be called when the user touches the Convert button in our user interface. This is declared using the IBAction keyword:
#import <UIKit/UIKit.h> @interface UnitConverterViewController : UIViewController { UITextField *tempText; UILabel *resultLabel; } @property (nonatomic, retain) IBOutlet UILabel *resultLabel; @property (nonatomic, retain) IBOutlet UITextField *tempText; - (IBAction)convertTemp:(id)sender; @end
We have now declared that our View Controller class contains a method called convertTemp. Having made that declaration, we now need to implement the method in the implementation source file. To do so, select the UnitConverterViewController.m file so that the contents appear in the editing pane:
#import "UnitConverterViewController.h" @implementation UnitConverterViewController /* // The designated initializer. Override to perform setup that is required before the view is loaded. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { // Custom initialization } return self; } */ /* // Implement loadView to create a view hierarchy programmatically, without using a nib. - (void)loadView { } */ /* // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; } */ /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [super dealloc]; } @end
The first step is to instruct Objective-C to synthesize accessor methods for our tempText and resultLabel objects and then to implement the convertTemp method. The relevant section of the UnitConverterViewController.m file should now read as follows:
#import "UnitConverterViewController.h" #import "UnitConverterViewController.h" @implementation UnitConverterViewController @synthesize resultLabel, tempText; - (void) convertTemp: (id) sender { double farenheit = [tempText.text doubleValue]; double celcius = (farenheit - 32) / 1.8; NSString *resultString = [[NSString alloc] initWithFormat: @"Celcius %f", celcius]; resultLabel.text = resultString; [resultString release]; } . . . .
Before we proceed it is probably a good idea to pause and explain what is happening in the above code. Those already familiar with Objective-C, however, may skip the next few paragraphs.
In this file we are implementing the convertTemp method that we declared in the .h file. This method takes as a single argument a reference to the sender. The sender is the object that triggered the called the method (in this case our button object). Whilst we won’t be using this object in the current example, this can be used to create a general purpose method in which the behavior of the method changes depending on how it was called. We could, for example, create two buttons labeled Convert to Fahrenheit and Convert to Celsius respectively, each of which calls the same convertTemp method. The method would then access the sender object to identify which button triggered the event and perform the corresponding type of unit conversion.
Next, the code declares a variable of type double in order to handle the fact that the user may have entered a floating point value. We then use dot notation to access the text property (which holds the text displayed in the text field) of the UITextField object. This property is itself an object of type NSString. The NSString object has a method, named doubleValue that converts the string value to a double. We therefore call this method on the text property and assign the result to our farenheit variable.
Having extracted the text entered by the user and converted it to a number we then perform the conversion to Celsius and store the result in another variable named celsius. Next, we create a new NSString object and initialize it with text that comprise the word Celsius and the result of our conversion. In so doing, declare a pointer to this new object and call it resultText.
Finally, we use dot notation to assign the new string to the text property of our UILabel object so that it is displayed the user. The last step releases the memory that was allocated to the object referenced by resultText since it is no longer needed.
We also need to modify the viewDidUnload and dealloc methods in this file to make sure we properly release the memory we allocated for our variables. Failure to do so will result in a memory leak in our application:
- (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.resultLabel = nil; self.tempText = nil; } - (void)dealloc { [resultLabel release]; [tempText release]; [super dealloc]; }
Before proceeding to the next section of this chapter, now is a good time to perform a build and run to make sure that no errors exist in the code. Click on the Build and Run button in the toolbar and correct any syntax errors that are reported.
Connecting the Actions and Outlets to the User Interface
The final step in developing our application is to connect the actions and outlets we have declared in the view controller class to the actual objects in the user interface view. Fortunately, this can all be done visually from within the Interface Builder tool.
With this in mind, double click on the UnitViewController.xib file to load the user interface into Interface Builder. We will begin by connecting the IBOutlet instance variables we created in our view controller class to the label and text field objects respectively. In the UnitViewController.xib window is an icon labeled File’s Owner. This icon represents the file containing the class that invokes the user interface contained in the NIB file when the application runs. In this instance it is the UnitConverterViewController class, where we declared our actions and outlets. To connect the resultLabel IBOutlet variable to the label object, hold down the Control key on the keyboard, click on the File’s Owner icon and drag the mouse to the location of the label object. A blue line will extend from the icon to the object as illustrated in the following figure:
Upon releasing the mouse button, Interface Builder will display a list of IBOutlet variables that match the type of the object selected:
Select resultLabel from the menu to complete the connection. Repeat these steps to connect the tempText outlet to the text field object. In order to view the connections you have created, display the Connections Inspector window by selecting Tools -> Connections Inspector and select the object for which you would like to review connection information. For example, the following figure shows the connection information for our label object:
The final step is to connect the button object to our convertTemp action method. Cocoa Touch objects typically have a wide range of events that can be triggered by the user. To obtain a full listing of the events available on a particular object, display the Connections Inspector (Tools -> Connections Inspector) and select the button object in the view window. Listed under Events in the connections window is a list of the events that can be triggered by the button object. In this instance, we are interested in the Touch Up Inside event. This event is triggered when a user touches the button and then releases their finger without first sliding it outside the boundaries of the button. When this action is performed we want our convertTemp method to be called. To make this connection, move the mouse pointer to the small circle next to the Touch Up Inside event in the connections dialog, click the mouse button and drag to the File’s Owner icon as illustrated in the following figure:
Releasing the mouse button over the File’s Owner icon will display a menu with a list of methods available in the view controller class. In our case the only method is convertTemp so select that method to initiate the connection. The event listing in the Connections dialog will subsequently update to reflect the new connection. Save the changes that you have made (File -> Save) and then exit from Interface Builder (Interface Builder -> Quit Interface Builder).
Building and Running the Finished Application
From within the Xcode project window click on Build and Run to compile the application and run it in the simulator. Once the application is running, click inside the text field and enter a Fahrenheit temperature. Next, click on the Convert button to display the equivalent temperature in Celsius. Assuming all went to plan your application should appear as outlined in the following figure:
Summary
In this chapter we have put into practice some of the theory covered in previous chapters, in particular the separation of the view from the controller, the use of subclassing and the implementation of the Target-Action pattern through the use of actions and outlets.
Learn SwiftUI and take your iOS Development to the Next Level |
Previous | Table of Contents | Next |
An Overview of the iPhone Application Development Architecture | Understanding iPhone Views, Windows and the View Hierarchy |