The Jungle, Part 4: Automatic Reference Counting


So far, we have been manually doing our memory management according to the basic rules established a long time ago. This method is still fine and is still the standard way to do it, but at WWDC 2011 Apple unveiled the revolutionary ARC technology. ARC stands for Automatic Reference Counting which does exactly what it sounds like—it automates the reference counting steps for you. It follows the exact same rules as the old memory management implementations, just automated for you. Rather than doing a runtime cleanup, as garbage collection does (a process which slows the running of your program at random intervals), the compiler looks at your code and automatically inserts retain, release, and autorelease calls as necessary. In this way, the performance of your application isn’t impacted in the least but it could save you a lot of work—after all, computers were designed to do menial labor like that.

In this post we’ll convert our existing project to ARC. Once we’re there, little work remains in memory management. Basically, ARC frees us from thinking about memory management at all, so upon finishing the conversion process we won’t be dealing with memory management anymore. Let’s get started.

Converting to ARC

Open our project and go to Edit > Refactor > Convert to Objective-C ARC…. You’ll get a sheet that says “Select Targets to Convert”; select the only option the list which should be called “SDK Demo.app (SDK Demo)”. Then click Precheck. Xcode will build your project then present a new sheet with an introduction.

ARC Conversion Intro Screen

ARC Conversion Intro Screen

Click Next and it’ll say “Generating Preview” for a few moments. The sheet will then expand, providing you with a two column view of your code.

Revisions Editor

Revisions Editor

To the very left you’ll see a list of files. In the main content, the left pane is the new code; the right pane is what you currently have. Changed lines are shaded in blue with a darker blue outline; changed code is highlighted in a salmon-esque color (by default). You can go through the list of files on the left (there should be five) and see the changes that ARC will do for you. You’ll notice that in the properties, retain is replaced by the new keyword strong; in the implementation files dealloc methods are stripped away, as are retain, release, and autorelease calls. The latter methods are now obsolete and you can’t actually call them anymore. Dealloc is not subject to the same treatment, but unless you’re doing some custom cleanup that goes beyond freeing memory, you shouldn’t have your own dealloc. Review the changes, and click Save in the lower-right.

Now Build & Run the app and when it comes in the Simulator you’ll notice that everything works just as before. …And, that’s it! Converting an app to ARC is just that easy. Note though, that in some cases the compiler may encounter cases where it can’t directly convert your code, in which case it’ll issue an error which you will have to address before you can run your app. This is often the case with files you might receive as part of someone else’s code library. In those cases, it is usually best to allow the original developer to support ARC or not as necessary. For the time being though, you can turn off ARC on a per-file basis to suppress those errors and run the code with the existing memory management code.

Selectively Turning Off ARC

To do so, select the Top Level project in the File Navigator, and select the name of your Target in the pane immediately to the right. Then in the main content pane, select Build Phases from the top, and expand the disclosure triangle next to “Compile Sources”.

Disable ARC per file by going to Build Phases > Compile Sources

Disable ARC per-file here

Select one or more files that you wish to exclude from ARC, and then hit Return on the keyboard. A little text field will appear. Inside that field, type

-fno-objc-arc

Which is a compiler flag to turn off ARC for those files. Hit Done and you’ll see the flag appear next to the files you’ve selected.

Compatibility

ARC code is fully supported in iOS 5, as well as OS X 10.7 Lion. In addition, most of ARC (exceptions detailed below) is supported in iOS 4 and OS X 10.6 Snow Leopard.

Language Changes

Converting a simple app like ours to ARC is not difficult. But ARC adds and modifies more of the Objective-C language, and these changes are worth talking about.

Autorelease Pools

Before ARC, you created autorelease pools like any other object and drained them when you’re done. However, because autorelease is no longer supported by the language, the old method wouldn’t work very well. So a new method had to be devised. Take a look at main.m, in the Supporting Files group. You’ll see code like this:

@autoreleasepool {
	    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
	}

There now exists a brace-delineated block for autorelease pools. All objects created within the block are autoreleased; the exact moment of autorelease is still determined by the ARC system. Therefore, in cases when you need to create an autorelease pool and release it independently from the main one (perhaps in a tight loop where you’re creating many autoreleased objects), just enclose the entire loop in the new block.

New Keywords

In our conversion process, retain was replaced by strong. This makes sense, as retain is no longer available. strong means the same thing—it states it is a strong relationship, that the class owns (and should “retain”) the property. Its counterpart is weak, which means that it is a weak reference. This is especially important when you have a loop reference—for example, if you have a chess game, then you might have a Grid class which keeps track of many Pieces , but at the same time each Piece has a reference back to its owning Grid. In such a case, if the references in both directions were strong, then neither object could be released because they’d both have a +1 retain count to each other, and you’ll leak the memory. A weak reference prevents this issue because it does not actually increment the retain count.

Note though that weak references are only supported on iOS 5 and OS X Lion. In previous versions, you’ll have to use unsafe_unretained, which is a more general keyword that offers you less “protection” by the compiler. It also maintains a weak reference, but it doesn’t guarantee anything else; therefore, you have to make sure that the value is valid when you access it to prevent bad access errors.

If you want to declare ivars with the same characteristics (and you should, just to be clear and to help the compiler out), prefix the keywords with two underscores:

__strong, __weak, __unsafe_unretained

Minor Issues

In my experiences I’ve come across two anomalies when using ARC:

  • If you are using a switch statement and you are declaring a new local variable as the first line of each case, you’ll have to surround each case with curly braces immediately after the colon and after the break; or you’ll get a warning about the variable being out of scope.
  • There may be presentation issues with UIPopoverController on the iPad, where the application may crash with the error that the popover had been released before the popover had been dismissed. I will continue to investigate this issue and file a bug report with Apple if necessary. For the moment, a solution seems to be to create a strong property for it to always keep a reference to it.

ARC greatly simplifies app development on Apple platforms and allows it to catch up to other languages with garbage collection, without the overhead imparted by garbage collection (which is in fact significant on a mobile device like the iPhone; even desktop OS X apps rarely use GC). There are more obscure aspects of ARC than I’ve covered here, including bridged casting which you might see if you start working with C code or some lower-level Foundation and Core Foundation classes. I’ll talk about them as we come to them. If you’re curious, feel free to check out the official ARC documentation.

And as usual, download the newest version of the project here.

The Jungle, Part 3: Flipping and Tab Bars


In previous tutorials we’ve been using some basic UIKit elements on standard views. Today, we’re going to add a tab bar to our application and wire it up; it’ll pave the way for some more compelling future expansions. Tab bars are more involved than the regular views we’ve been using; in additional to simply setting and accessing properties, we also have to contend with a specific controller and design pattern, as well as delegates. Let’s get started.

Tab Bar Controllers

In most cases, tab bars are used with UITabBarController. You may use an independent tab bar if you’re using it to display the same information in the same view, just sorted differently, for example. However, if you plan on swapping views around, you should use the controller because it manages the swapping mechanism and paradigms for you.

According to Apple’s documentation, “The UITabBarController class implements a specialized view controller that manages a radio-style selection interface.…[Y]ou use instances of it as-is to present an interface that allows the user to choose between different modes of operation. This tab bar interface displays tabs at the bottom of the window for selecting between the different modes and for displaying the views for that mode.” Tab bars are prevalent in many of the standard iOS apps, including Clock and Music.

Rather than accessing the tab bar itself (Apple claims that “You should never access the tab bar view of a tab bar controller directly”), you should pass the tab bar controller an array of UIViewControllers (or subclasses of it) to a property called viewControllers. The ordering in the array determines the order of the tabs. Alternatively, you can configure the tabs’ order, title and icon in Interface Builder. When a tab is selected, the controller will load the root view controller for the corresponding tab in the main content view. This means that if you’ve drilled into a navigation stack (which we will be covering in a future post), you will be returned back to the top of the stack, even if you tap the tab you’re currently on. By default the state of the view is not saved; however, you can do initial configuration in your own controllers to do so. Any class can become the delegate of the tab bar and receive notifications about changes. As per the nature of delegation, this is completely optional; proper configuration in Interface Builder means that the view swapping will work fine without having to create a delegate.

Getting Started

Today we’ll be creating a second view controller to have something to switch to, and then we’ll create a tab bar controller and implement the switching. The process is really intuitive once you know where to begin—so let’s begin!

Begin by creating a New File in Xcode. Make sure UIViewController subclass is selected in the Template pane and click Next.

New UIViewController Subclass File

The Class name should be FlipViewController. It should be a Subclass of UIViewController, not be targeted for iPad, and have a XIB for the user interface. Click Next, and then Create (unless you want to change the file save location or the group the new files get placed under—go ahead). You’ll get three new files—FlipViewController.h, FlipViewController.m, and FlipViewController.xib. Here, we’ll create a very simple view that implements a flip-over view like you might see in the Weather or Stocks app.

Start by creating two UIViews in the header, making sure to declare them as IBOutlets and calling them frontView and backView. In the XIB, drag out two UIViews from the Library. Connect the views—Control drag from the File’s Owner (the first icon on the strip to the left) down to the views and make the connections as you’re used to. In the view you’ve designated to be the front, choose a dark color for the Background and then drag out a UIButton. In the Attributes Inspector, next to Type, select Info Light to get you the little “i” button that you’ve seen around iOS. Align that with the bottom right corner of the view. Also in each view, add a label with “Front view” and “Back view”, just for reference. In your back view, add a regular UIButton in the upper left corner with the text “Done”. This follows the standard UI design pattern—if you’re using an info button to go to another view, the info button should be in the lower-right corner; a Done button to return sometimes may be in a nav bar (which we’ll cover in a later post), but is usually in the upper-left corner.

Now connect the actions. The method to go to the back view should be called flipToBack: and take a sender. The method to go back should be called flipToFront: and also take a sender. Of course, make sure the methods are defined in the header, save both the header and the XIB, and go to the .m file. Synthesize the frontView and backView. Add the following line to viewDidLoad after the existing code:

[self.view addSubview:self.frontView];

This will make the frontView visible. Next we’ll implement the flipping methods:

- (IBAction)flipToBack:(id)sender {
	[UIView transitionFromView:self.frontView toView:self.backView
			  duration:1.0 
			   options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromRight)
		        completion:NULL];
}

- (IBAction)flipToFront:(id)sender {
	[UIView transitionFromView:self.backView toView:self.frontView
			  duration:1.0 
			   options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft)
			completion:NULL];
}

These methods are rather similar. We’re calling new UIView animation methods introduced with iOS 4 that replaced a more verbose and complicated system from before; these methods also take advantage of blocks which were also new with iOS 4 and, again, will be covered in the future. Here, we’re asking it to transition from one view to another. It’ll draw one view, and then animate in the other based on the options you pass in. The duration is the length of the time you want the animation to take. The options are a list of options (at the end of the page) you can pass in; you can combine them any way you want (barring conflicting options) using the bitwise OR operator ( | ) and enclosing the group in parentheses. Here, we’re telling the animation to use a smooth, natural ease-in and then ease-out at the end. Other options include a linear path, or just ease-in or just ease-out. Finally, you can pass a block in when the animation completes; we’re not going to do anything here for the moment. Our view controller is done—so let’s build our tab bar controller.

Creating the Tab Bar Controller

Open AppDelegate.h and add a tab bar controller property:

@property (nonatomic, retain) UITabBarController *tabBarController;

Synthesize the property. In the implementation, import FlipViewController.h and then release tabBarController in the dealloc method. Then create the tab bar controller:

RootViewController *rootViewController = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
rootViewController.title = @"Root View Controller";
FlipViewController *flipViewController = [[FlipViewController alloc] initWithNibName:@"FlipViewController" bundle:nil];
flipViewController.title = @"Flip View Controller";
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:rootViewController, flipViewController, nil];
self.window.rootViewController = self.tabBarController;

First we create the view controllers and set their title. The title of the view controllers is the title that the tab bar displays. We then create a tab bar controller and set the view controllers as an array. You then set the root view controller as before.

If you run the program now, you’ll get a working app with a tab bar at the bottom. Switch between the tabs and go to the Flip View Controller—and you’ll realize the issue we have. The tab bar is in fact obscuring the flip button. The solution is rather simple—go to FlipViewController.xib.

Simulated Tab Bar & Moved Button

Select the front view and move up the Info button. Select the front view itself and go to the Attributes Inspector. Under Simulated Metrics, select Tab Bar from the list next to Bottom Bar. Using that as a guide, position the info button. Build and run again. The flip view will work as you’ve seen in other Apple apps.

Apple’s Progression

In iOS 5, Apple’s recommended way to create controllers is through code in the App Delegate. In previous versions you’d have started with a MainWindow.xib which you can configure in Interface Builder. I feel that because from now on all project templates will feature this new code, it is important to get to know it.

You can download the current iteration of the file here.

The Jungle, Part 2: More UI Elements


This week we’re going to continue from Part 1 and explore additional UI elements. Before we begin though, a bit of (old) news—iOS 5.0 was released on Wednesday, and with it Xcode 4.2. As such, from now on I will be working with Xcode 4.2 and the 5.0 SDK. Once we get the basics down we can discuss compatibility with older versions and how to check the system version, but we’ll use the newest and greatest for now.

As usual, Xcode 4.2 is available on the App Store. The Xcode 4.2 build is 4D199 (you’d be surprised how difficult it was to find that anywhere else—important if you’re upgrading from a Developer Preview), which you can get either from the Welcome screen or by going to Xcode > About Xcode. There is an installation issue floating around in which you may be constantly installing 4.1. In that case, I’d recommend uninstalling Xcode first, and then running the “Install Xcode.app” that the App Store downloads. That fixed the problem for me.

Once Xcode is installed, open up the project from last time, and dive into RootViewController.xib.

Interface First

With Xcode 4’s integration of Interface Builder and the code, it breaks down the boundaries of code versus interface and allows you to easily jump back and forth as ideas come into your head. This time, we’ll put together the interface first and then hook it up to code—even to code that doesn’t exist yet.

First, start by moving the text field up a bit so we have some more vertical room below. Drag it up, following the blue guidelines, until it snaps into place a bit below the button.

Segmented Control Settings

Segmented Control Properties


Drag out a Segmented Control and place it near the middle of the screen. Extend the width until you get to the blue guidelines at the left and right of the screen. Go to the Attributes Inspector, and use the Segment pop-up menu to select “Segment 0 – First”. Underneath, change the Title to “Colors” and watch the change propagate to the UI as well. Change the title of the second segment to “Progress” using the pop-up to change the segment.

The segmented control works similar to the tabs you see across OS X, especially in System Preferences—users expect them to “swap” view content to something else, and they are usually placed above the content that you expect to swap. For simpler views it may be easy enough to place views overlapping each other and hide or show them as necessary (UIView has a hidden property that can be set to YES or NO). However, this is quite cumbersome and doesn’t scale very well. A better alternative is to create a plain UIView and set that to individual views as necessary. Therefore, drag out a basic UIView, align it underneath the segmented control, make it as wide, and drag it down until the blue guidelines appear at the bottom. It should look like this:

Main View

New Main View

Now select the view, and copy (Command-C) it. Click outside of the main view (on the main canvas area) and paste (Command-V). You should get a blank view the size of the original, floating around on the canvas. Click on the canvas again, and paste another view. They will be the views you’ll swap in with the segmented control.

Set the background of both views to Group Table View Background Color. It may be easier to set their color to transparent, but this has a major impact on drawing performance—it is much faster to set a specific opaque color.

Drag out UI elements from the Library to build two views like you see here. The exact positioning doesn’t matter, but make sure to get the correct elements. The first view contains a standard UIView at the top with the Background color set to 50% gray (click on the left part of the Background control in the inspector to get the standard OS X color picker). Underneath are four labels, right-aligned with a standard shadow (most UI text in iOS is drawn with a shadow) and four sliders next to the labels. Here, don’t worry about the edge guidelines—go right up to the edge of the view. You’ve already accounted for the edge guides in the main view.

Sliders View

Sliders View

The second view contains a few more elements—a bold-font label, a regular-font label, a stepper control (new in iOS 5), another bold-font label, a switch, a progress view, and a button. None of the views except the labels have been customized away from their default appearance. The regular label should have its text set to 0%, rather than the standard “Label”.

Progress View

Progress View

We have to set some properties on the stepper for it to work with us. Select it then open the Utilities Inspector. The Minimum value should stay at 0 but the Maximum should go up to 100. Increase the Step to 10 to have it change by 10, from 0 to to 100. Also, select the progress view and set the Progress to 0.

Making Connections

Our interface design is now done, so let’s make the connections. Open the Assistant editor (The second button in the “Editor” group in the toolbar). Make sure the file in the right pane is RootViewController.h (you can use the jump bar). Control-drag from the blank view in the main view to a blank line in your header. You’ll get a gray rectangle that says “Insert Outlet or Outlet Collection”.

New Connection

New Connection

Let go, and you get a little popup that lets you configure the outlet name and type. Call the outlet sectionView. Click Connect, and a new property will appear with a blue flash. To connect the other smaller views, you’ll have to Control-drag from the icons on the left edge of the IB view to the code. See screenshot below:

Connection from Sidebar

Connection from Sidebar

The view with the sliders should be called colorsView; the other should be called progressView.

New Outlet Popup

New Outlet Popup

Then, connect all five elements in the colorsView to the header as well—displayColor, redSlider, greenSlider, blueSlider, and alphaSlider. The progressView isn’t quite as straight-forward, but it’s not difficult. Connect the label next to “Fill:” as amountLabel, the stepper as amountStepper, the switch as animatedSwitch, and the progress view as progressIndicator.

Now we’ll connect the actions. Start off with one of the sliders—right click on one of them, and scroll down until you find the Value Changed Event.

Control Events

Control Events

Click and drag from the circle to the right until you come to a new line underneath the actions—you’ll get a blue line you’ll be familiar with now. You’ll get the gray rectangle that says “Insert Action” this time. In the pop-up, call the method colorSliderChanged. Next to Arguments, select “None” from the menu. Click Connect. Now do the same with the other sliders, but rather than dragging to a new line, drag until the colorSliderChanged method becomes highlighted. You’ll connect to that method, rather than creating a new one.

We’ll use a slightly different way to connect the button’s action. Control-drag from the “Reset” button to a new line underneath the actions. The default option will connect an outlet, but we want an action. From the Connections popup menu, choose Action instead. Change the name to resetContent.

New Action Popup

New Action Popup

Note that the event is “Touch Up Inside”, the default for buttons. Arguments should again be None.

Connect the stepper control in the same way. Name should be fillAmountChanged; Arguments should be None. Note the default event, “Value Changed” for the segmented control. Arguments should be None.

Finally, we’ll have to connect the segmented control as well. Follow the same process as before. The name should be sectionChanged. Arguments should be Sender.

Writing the Code

Open RootViewController.m and scroll through the file. You’ll note a bunch of synthesized properties, ivars added to the dealloc method, and blank methods for all the actions you just connected. This saves us a lot of boilerplate code and lets us get right down to work. Put in a blank line in the colorSliderChanged method after the opening brace. This method will change the fill color of that gray rectangle based on the slider values, converting to an RGB-A color. Note though that this isn’t necessarily a recommended way to do it—it can be slow on older devices.

We’ll create four float variables to hold the values of the sliders, then create a UIColor instance based off the floats. Then we assign that as the background color of the blank view. The code for the method:

- (IBAction)colorSliderChanged {
	float redColor = self.redSlider.value;
	float greenColor = self.greenSlider.value;
	float blueColor = self.blueSlider.value;
	float alphaValue = self.alphaSlider.value;
	UIColor *newBackground = [UIColor colorWithRed:redColor green:greenColor blue:blueColor alpha:alphaValue];
	self.displayColor.backgroundColor = newBackground;
}

Next, let’s handle the case where the stepper’s value is changed. First we need to update the label, which we’ve already connected. We grab the stepper’s value, and create a string out of that. Then we assign the string to the label’s text property. Next, we’ll update the progress bar. We use the switch’s on property to determine if we should animate, and use the stepper’s value to determine where to fill to.

- (IBAction)fillAmountChanged {
	NSInteger amount = (NSInteger)self.amountStepper.value;
	// You need two percents to "escape" it and actually display a percent sign
	NSString *amountString = [NSString stringWithFormat:@"%d%%", amount];
	self.amountLabel.text = amountString;
	[self.progressIndicator setProgress:((float)amount / 100.0) animated:self.animatedSwitch.on];
}

The reset method is easy. We’re just assigning some properties.

- (IBAction)resetContent:(id)sender {
	self.amountStepper.value = 0.0;
	self.amountLabel.text = @"0%";
	[self.animatedSwitch setOn:YES animated:YES];
	[self.progressIndicator setProgress:0.0 animated:YES];
}

There’s one last bit that we have to handle—view swapping. Right now if you run the project you’ll just get a white rectangle on screen and you’ll not get any of our new code. A simple method like this will fix the issue:

- (IBAction)sectionChanged:(id)sender {
	UISegmentedControl *sectionControl = (UISegmentedControl *)sender;
	if (sectionControl.selectedSegmentIndex == 0)		// Colors section
		[self.sectionView addSubview:self.colorsView];
	else if (sectionControl.selectedSegmentIndex == 1)	// Progress section
		[self.sectionView addSubview:self.progressView];
}

In this method, we’re simply adding the correct view to our main placeholder view as necessary.

Now the code is complete. But if you run it, you’ll see an obvious problem—the first time you run, you’ll get a blank white rectangle. It’s only when you select a segment in the segmented control that you get a view. Let’s fix that by adding this one line to viewDidLoad:

[self.sectionView addSubview:self.colorsView];

We know that the first view to load will be the colors view, so automatically we’ll add that view. If you build and run now, you’ll get the fully working app.

UIKit From Here

As you’ve seen, using UIKit controls is rarely very difficult or involved. 85% of the time you’re just setting and getting properties on the controls. Some of them have a custom setter that allows you to animate the change. There are some other controls that we haven’t really covered, but they’re not difficult. We’ll get into putting a tab bar into our application (and its corresponding controller) next time. For now though, if you would like to experiment around, feel free. Also check out Apple’s documentation. You can download this version of the sample project here.

The Jungle, Part 1: Basic UI Elements


Recently, we’ve been discussing concepts. This week, we’re going to take a break from that and start working on an app that will cover many of the high-level topics in the iOS SDK and allow you to build many interfaces with Interface Builder. We’ll start from a bare-bones project template and work our way up from there.

I’m running Xcode 4.1 on Mac OS X 10.7 Lion. Xcode 4 is available on the Mac App Store for both Snow Leopard and Lion. You can use Xcode 3, but some things (especially Interface Builder) will be different, so you may have to “translate” some of the screenshots. Nevertheless, the procedure in Xcode 3 is still similar, just in different places. Xcode 4 is the future, and despite the criticisms, does have some really nice features. And it includes the latest features, many of which make coding much easier. So fire up Xcode, and let’s get started.

Setting Up the Project

When you first start Xcode 4, you’ll be presented with the Welcome to Xcode window. As you create more projects, the list to the right will get populated with those projects. For now, read the texts if you wish, but click on “Create a new Xcode project” to get started.

Xcode 4 Welcome Window

Xcode 4.1 Welcome Window

A new window will appear, and a modal sheet will slide down. From the left column, under iOS, select “Application”. Then, in the top-right pane select “Window-Based Application” (it may be called “Empty Application”). Click Next.

For Product Name:, call it “SDK Demo”. You can call it something else, but you’ll have to make adjustments down the road. You can ignore the other options; we’ll talk about them in later posts. Click Next and find a place to save your project.

Xcode Main Project Window

Xcode Main Project Window

Xcode should now present the main project window, and your project has been set up. At this point you can click on the circular Run button in the top-left of the main Xcode window, which will launch your app in the iPhone Simulator.

All you’ll see at the moment is a blank white screen—that’s because we haven’t done anything yet. Let’s do that now.

The Root View Controller

All iOS applications should have a root view controller. There are books and tutorials online that tell you to do your whole interface in the app delegate (I’ll talk about this below)—which you can, but it is not recommended for anything but the simplest project. Sometime in the future, applications will be expected to have a root view controller—they will no longer be optional. It’s just a good idea. So let’s create one.

In Xcode, go to File > New > New File… (Command-N). On the left, select Cocoa Touch under iOS and select the second option, UIViewController subclass. Click Next. For Class, call it RootViewController. Make sure it’s a subclass of UIViewController, and that it comes with a XIB for the user interface. Click Next, and accept the default save location. Click Create.

New File View

New File Picker

New File Name

Name new file

In the File Navigator, click on AppDelegate.h. There is some existing code provided by the template; leave that there. Note that the provided code may differ across versions of Xcode, but the general meaning and end results are the same. Add the following lines to the existing code to set up the header file.

#import <UIKit/UIKit.h> 
@class RootViewController;    // Add this line
@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) RootViewController *rootViewController;    // Add this line too 
@end

Switch to the .m file, either by selecting the file in the File Navigator, or pressing Command-Control-↑. In this case, there’s a lot of comments and sample code that we aren’t going to use now. You can keep it if you wish, or replace the file with this code:

#import "AppDelegate.h"
#import "RootViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize rootViewController = _rootViewController;

- (void)dealloc {
	[_window release];
	[_rootViewController release];
    [super dealloc];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
	_rootViewController = [[RootViewController alloc] init];
	self.window.rootViewController = self.rootViewController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end

The code of interest is in the application:DidFinishLaunchingWithOptions: method. Every app starts off as a window, and that’s what the first line does—it creates a window that is the size of the screen, and autoreleases it. That line, and the next, which sets the window’s background color to white, is provided by the template. The next two lines are what’s important here—you’re initializing the RootViewController instance, and assigning it to the window’s rootViewController property, which was new in iOS 4.0. In iOS 3.x and earlier, you couldn’t directly set the view controller; you had to set the view instead.

[_window addSubview:self.rootViewController.view];

The last two lines actually display the window, and return YES. If you return NO, the app will then be quit. The app setup is now done. Running the app, you won’t notice any visual difference, but the setup is indeed complete.

The App Delegate

Now is a good place to take a detour and talk about the app delegate. This class, created with every project, is the interface between the iOS system and your app. When the system has finished loading the app, the application:didFinishLaunchingWithOptions: method is called. In many cases, you can ignore the arguments to the method. Generally, here you perform some basic setup, including creating the main window and the root view controller as you saw above. In a game, you may also choose to initialize timers or graphics engines here, but make sure you don’t do something too time-intensive, or your app will appear to hang.

Other situations in which app delegate methods may be called include times when your app is being quit, in which you have a few seconds to do some last-minute processing or to save state, or when your app goes into or comes from the background. “The background” is only present in a multi-tasking environment (iOS 4.0 or higher on some iOS devices), when your app may be dismissed without being quit. In those cases, you may want to save state, or pause game timers, or close network connections.

Do note that in all these cases, you have limited time to do your processing, so you shouldn’t be calculating pi to 15 million digits here. Taking too long to do any processing will make your app appear to hang, and the system will try to avoid that.

A technical note: how does the system know which class the app delegate is? If you want to know, open up the Supporting Files group in the File Navigator. Select the main.m file. Inside, you’ll find this code:

UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))

That fourth argument to the UIApplicationMain() function is the name of the app delegate class, passed in as an NSString. NSStringFromClass() takes a Class object (found by invoking the +class method on any class) and returns the string with its name. That’s how the system finds the app delegate.

Creating the Root View Controller

In this initial revision of our app, we’ll add in some basic functionality that shows off some basic UIKit elements. We will also be exploring Xcode 4’s new Interface Builder, included within Xcode’s main window.

Open up RootViewController.h and put in the following code.

#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
	int buttonPressCount;
}

@property (nonatomic, retain) IBOutlet UILabel *buttonPressLabel;
@property (nonatomic, retain) IBOutlet UILabel *echoLabel;

- (IBAction)simpleButtonPressed:(id)sender;
- (IBAction)textFieldTextDidChange:(id)sender;
@end

You add two label outlets and two action methods. The IBOutlet and IBAction qualifiers don’t affect your code at all, but they are a “hint” to Interface Builder as connect points.

Interestingly, the action methods take one argument, of a generic type. This sender object is the object that triggered the event. You could have written the method without the argument, but then you would not know which object triggered the method. In some cases, you can determine what course of action you want to take based on the sender object (you can hook up one action method to multiple objects), or access some property of the sender. We will use the second reason. By doing so, we can avoid declaring two additional outlets, only to access their properties. As you gain more experience, you’ll get a feel for which properties you need to declare and hook up, and which you can obtain from method arguments, cleaning up your code.

Setting Up the Interface

Blank XIB

Blank window with Utilities

UIView Attributes

UIView Attributes

With your declarations done, switch over to RootViewController.xib. Open up the Utilities pane by clicking on the third button in the View group in the toolbar, or by pressing Command-Option-4. Click in the main view to select it and bring up the Attributes.

The white background is a bit garish, so click on the double arrows at the right end of the Background popup. If you click in the middle, you’ll get the standard color picker. If you click on the double arrows, you get a list of recent colors, and some options that you can’t get in the color picker.

Color Choices

Color choices

Select the “Group Table View Background Color”. The main view will take on a blue-and-white pinstripe appearance. That’s better.

The bottom part of the Utilities pane has four small icons. Select the third one to bring up a list of UI elements you can use. The search bar at the bottom allows you to filter the results. Drag out a button into the main view.

UI Elements Library

UI elements library

As you drag, you’ll see blue guidelines appear—those are Apple’s recommendations for spacing, keeping your UI elements away from the edges of the screen, where they may be hard to hit, and away from each other, to prevent the wrong element from being touched. A guideline will appear at the middle of the screen. Align the button with the top guideline and down the middle.

Blue Guidelines

Blue guidelines

Using the horizontal drag handles, make the button about 160 pixels wide (there will be an overlay telling you the size; don’t worry about the exact value), and bring it back to the center using the guides. Double click in the center of the button to bring up a text-editing field. Type in “Press Me!” or something similar. Click off the main view to deselect the button.

Finalized Button

Finalized button

Drag out a label and center it. Move it up toward the button until the guide appears underneath the button, and leave it there. Again, make it 128 pixels wide and center it. Using the Attributes inspector, set the following properties:

Label Attributes

Label attributes

  • Set Alignment to centered
  • Make the Text Color Black (or Default)
  • Make the Shadow color White
  • Make the Shadow Offset 0 Horizontal, 1 Vertical (the default is -1)
  • Clear out the text, so that the label isn’t displaying “Label”

Drag out a text field. Make it 160 pixels wide, centered horizontally. The vertical position doesn’t really matter too much. Keep the default attributes for now; feel free to play around with them later. Now hold down the Option key. Click and drag the original label to a position underneath the text field. You’ll make a copy of the original label, including all the attributes.

Making Connections

Connections Inspector

Connections inspector

Now that we have the UI elements laid out, we’re going to connect it to our outlets and actions. There are many ways to do that; for now, because our inspectors are open, we’re going to use those. We’ll cover other methods in future posts.

Open the Connections inspector (the sixth option in the Utilities sidebar) or press Command-Option-6. Select the first object along the bar to the left—it looks like a light orange cube and when you mouse over it, the pop-up says “File’s Owner.” This is a proxy object (I’ll talk about that in a future post when we explore Xcode) that references the owner of the XIB—in this case, RootViewController.h. Selecting that, some outlets show up in the connections inspector. First we’ll wire up the outlets.

Click in the little circle to the right of the buttonPressLabelentry. Drag the cursor over to the label below the button, and let go. As you drag, you’ll see a blue line appear, starting from that little circle.

Visual connection—blue line

Blue line shows connection

The connection will show up in the connection inspector. Repeat the process for the echoLabel, connecting it to the label beneath the text field.

Shows Connection

Visual connection

Under the Received Actions section, drag from simpleButtonProssed: to the button in your interface. This time, you’re connecting an action; when you release, you’ll get a list of eventsthat the button can respond to. You can have different methods trigger based on what type of touch input the button receives.

UIControl Events List

Events list

Select “Touch Up Inside”, which means that the action will only be triggered if a finger touches down and up—what you would naturally do—all within the borders of the button. This is the most commonly-used choice for buttons. Connect textFieldTextDidChange: to the text field, but this time select Editing Changed for the event. This will have the method be called every time the text inside the text field changes—it’ll get called every time the user types a letter or hits the backspace.

Having wired up the entire interface, return to the code—open RootViewController.m.

Coding the Actions

You can replace the existing code with the following:

#import "RootViewController.h"

@implementation RootViewController
@synthesize buttonPressLabel = _buttonPressLabel;
@synthesize echoLabel = _echoLabel;

#pragma mark - Memory Management
- (void)dealloc {
	[_buttonPressLabel release];
	[_echoLabel release];
}

#pragma mark - View lifecycle
- (void)viewDidLoad {
	buttonPressCount = 0;
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view to relieve memory usage
	self.buttonPressLabel = nil;
	self.echoLabel = nil;
}

#pragma mark - Action Methods
- (IBAction)simpleButtonPressed:(id)sender {
	buttonPressCount++;
	NSString *pluralHandlerString = (buttonPressCount == 1) ? @"time" : @"times";
	NSString *displayString = [[NSString alloc] initWithFormat:@"Button pressed %d %@", buttonPressCount, pluralHandlerString];
	self.buttonPressLabel.text = displayString;
	[displayString release];
}

- (IBAction)textFieldTextDidChange:(id)sender {
	UITextField *textField = (UITextField *)sender;
	NSString *textFieldText = textField.text;
	self.echoLabel.text = textFieldText;
}
@end

The beginning isn’t of much interest—we’re importing files, synthesizing properties, and dealing with memory management. The viewDidLoad method is where you usually do some setup—initialize instance variables, or set some properties on UI elements. viewDidUnload is where you release your retained subviews (and any other properties that you initialize or create in viewDidLoad) to free up that memory, when your view disappears. The action methods warrant some discussion.

The first action method simply keeps track of the number of times the button is pressed and displays that. It uses the buttonPressCount instance variable, incrementing it each time the button is pressed. There is initially a string that uses the conditional operator to determine whether or not to use the plural form of “time.” Then that string is plugged into the final string, along with the run count, to form the displayString. This is then assigned to the text property of the corresponding label, which causes the text to be displayed. The string is released according to memory management rules.

The second action method uses the sender argument, first casting it to a UITextField. It grabs the current text value from the text field—this method is called every time the text changes, so the text value will be different every time, and then this text is assigned to the label’s text property as before. The text field manages its text property, so you don’t have to release it.

The Finished Product

Finished Project Version 1 Screenshot

Finished product

At this point, the first version of our demo app is done. Hit Run in the toolbar, and it should bring up the iPhone Simulator. Play around with the button presses and the text field. If you have build issues or warnings, post them in the comments or email me, and I’ll get back to you as soon as possible.

One More Thing

Once the keyboard shows up, you can’t get rid of it without quitting the app. Let’s fix that.

Add an action method called dismissKeyboard: to the header, which takes one sender. The sender will be the text field. Connect the method in IB to the text field’s Did End on Exit event. The implementation of the method should be like this:

- (IBAction)dismissKeyboard:(id)sender { 
    [(UITextField *)sender resignFirstResponder];
}

Now, if you hit the Return key on the keypad, the keyboard will slide away. The important part is the resignFirstResponder method call, which in the case of a text field will also dismiss the keyboard.

And that’s all there is to it.

Project Download

You can download the source for this project here.

  • Welcome

    My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

  • Contact Me

    If you need to contact me for any reason, feel free to send me an email.
  • The Giving Spirit

    If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

  • Roadmap

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 220 other followers

  • Back to the Past

    May 2020
    S M T W T F S
     12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31  
  • Time Machine

  • You count!

    • 622,649 views
  • Worldwide Stats

    free counters
%d bloggers like this: