Advertisements

Working With Web Data


Apps become a lot more interesting when we connect them to the internet. They’re able to fetch live data, update that data, and interact with other people and devices. In this post, we’ll look at how to pull data from the internet.

We’ll be working with JSON data. JSON, which stands for JavaScript Object Notation, is one of two popular formats for reading general data from the web (XML is the other). JSON has the advantage of being a lot more terse than XML, and is a lot easier for websites themselves to work with. iOS includes libraries to work with both data types.

Let’s begin by creating a new project—I’ll call it JSONRead—and making our main view controller a subcontroller of UITableViewController. We’ll begin by reading JSON data from the web, parsing it, and saving it to a data object in our controller. In fact, here is the JSON we’ll be using (see below for more on the JSON syntax and how to generate it):

[
    {
        "fname": "Nadene",
        "lname": "Feehan",
        "email": "nadene.feehan@gmail.com",
        "phone": "(152) 555-5321"
    },
    {
        "fname": "Marleen",
        "lname": "Harding",
        "email": "marleen@mharding.com",
        "phone": "(134) 555-1134"
    },
    {
        "fname": "Moon",
        "lname": "Larusso",
        "email": "lunarguy@hotmail.com",
        "phone": "(123) 456-5432"
    },
    {
        "fname": "Scotty",
        "lname": "Wollman",
        "email": [
            "scotty@wollman.com",
            "beammeup@gmail.com",
            "s.wollman@bigcorp.com"
        ],
        "phone": "(152) 555-5321"
    },
    {
        "fname": "Celest",
        "lname": "Feehan",
        "email": "celestial@gmail.com",
        "phone": "(098) 765-4321"
    },
    {
        "fname": "Saggarth",
        "lname": "Ramakristhan",
        "email": "sagar@gmail.com",
        "phone": "(012) 345-4321"
    }
]

Next, in the .m file, we’ll define a link to the file, and a private interface to hold our properties:

#define JSON_FILE_URL @"https://dl.dropboxusercontent.com/u/7828009/JSONRead.json"

@interface PeopleListViewController ()
@property (strong, nonatomic) NSArray *names;
@property (strong, nonatomic) NSArray *data;
@end

Synthesize these properties. Next, we’ll load the data and parse it into basic data types in viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
	self.title = @"JSONRead";
	
	// Download JSON
	NSData *JSONData = [NSData dataWithContentsOfURL:[NSURL URLWithString:JSON_FILE_URL]];
	// Parse JSON
	NSArray *jsonResult = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:nil];
	self.data = jsonResult;
	NSMutableArray *_names = [NSMutableArray array];
	for (id item in jsonResult)
		[_names addObject:[NSString stringWithFormat:@"%@ %@", item[@"fname"], item[@"lname"]]];
	self.names = _names;
}

Downloading data from the internet starts by creating an NSURL (which can be instantiated from an NSString), then getting an instance of NSData from that URL. We then use a method that Apple has provided since iOS 5.0 to parse the JSON. Note that we’re casting to NSArray—NSDictionary is another common option, and we’ll talk about how to tell which to use below. Finally, we simply loop through each element in the array, and pull out certain properties to generate an array of names, which we display in our table view. In this case, each element in the parsed array is a dictionary, so we’re using the new literal syntax to access a key-value pair in each item.

Our table view data source is pretty simple—one section, with as many rows as we have names. Our table view cells display the names that correspond to the indexPath.row, and in the delegate, when you select a cell we create a detail view controller (make sure to import the header), pass in the item from our data array that corresponds to the selected indexPath.row, and push the detail view controller onto the navigation stack.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return [self.names count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    
    cell.textLabel.text = self.names[indexPath.row];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
	PeopleDetailsViewController *detailViewController = [[PeopleDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped];
	detailViewController.details = self.data[indexPath.row];
	[self.navigationController pushViewController:detailViewController animated:YES];
}

JSON Format

JSON begins with key-value pairs:

"fname": "Nadene"

These values can be an integer or floating point, string, boolean (true or false), array, object, or null. A list of key-value pairs is separated by a comma.

JSON values can be an array—a list of values (any of the above values are valid in an array) wrapped in square brackets. Our JSON sample is in fact a single array, as shown by the starting and ending brackets, and this knowledge allows us to choose NSArray as the object that the JSON gets parsed to.

JSON values can also be an object (objects in a Javascript context), which is rather like a grouped set of key-value pairs, wrapped in curly braces. Groups are used in our sample JSON to group the key-value pairs of each person as a separate element inside the array.

So for our sample JSON, we have a single array containing six objects. Each object has four key-value pairs, and for Scotty, he has an array for his email addresses, signifying that he has multiple emails.

Detail view

The details view controller is also a subclass of UITableViewController, with an NSDictionary for the details which we pass in:

@interface PeopleDetailsViewController : UITableViewController
@property (strong, nonatomic) NSDictionary *details;
@end

We define a simple method to generate a name based on the person’s first name and last name

- (NSString *)name {
	return [NSString stringWithFormat:@"%@ %@", self.details[@"fname"], self.details[@"lname"]];
}

and use it to set the title in viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
	self.title = [self name];
}

Our table view data source is, again, pretty simple—1 section, 3 rows, and specific content for each row:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return 3;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
	if (!cell)
		cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    
    switch (indexPath.row) {
		case 0:
			cell.textLabel.text = [self name];
			cell.detailTextLabel.text = @"Name";
			break;
		case 1: {
			NSString *email = [details objectForKey:@"email"];
			if (!email)
				email = @"No email";
			if ([email isKindOfClass:[NSArray class]])
				email = @"<Multiple emails>";
			cell.textLabel.text = email;
			cell.detailTextLabel.text = @"Email";
			break;
		}
		case 2:
			cell.textLabel.text = self.details[@"phone"];
			cell.detailTextLabel.text = @"Phone";
			break;
		default:
			break;
	}
    
    return cell;
}

This is all the code we need. Click Run, and we’ll see a simple contact manager.

Contacts—People List

Contacts—People List

Contacts—Detail View

Contacts—Detail View

Generating JSON

A JSON string is valid Javascript syntax, so it can be used as a Javascript object or array. To turn a string into Javascript, use JSON.parse(text, function(key, value)); to convert an object into a JSON string, use JSON.stringify(object). jQuery also allows you to serialize() a form, which you can then pass to a server. This is really useful when you want to submit a form via AJAX.

In Ruby, the new hash notation is the same as JSON, which makes things easier. object.to_json will create a JSON string out of an object, and JSON.parse(object) will create an object from JSON.

In PHP, json_encode() takes an object or array (linear or associative) and returns a JSON string. json_decode() will return an object or linear array depending on the JSON; an optional second parameter allows you to force an associative array where it would otherwise return an object.

Download project files

Advertisements

The Jungle, Part 8: Basic Data Persistence


Persistence is the ability to save files and content to disk, so that you can read it out later and be able to save stuff. Most apps use persistence of some sort; even if your app doesn’t create files, you may still have settings and configurations you want to save. iOS provides a number of formats to save different types of data. These include user defaults, property lists (plist), archived objects, text files, XML, SQL databases, and Core Data. In this post, we’ll talk about the first four formats. XML will be covered in a separate extension, as it uses additional software beyond the standard SDK, and Core Data is complex enough to be the subject of its own post. With the power and flexibility that Core Data provides, there is little need for standard SQL databases, and so we will not cover it here; there are many good tutorials on SQL on the Internet. Let’s get started.

Building the App

Open Xcode, create a new Single View Application, and call it “Persistence”. Make sure to not use storyboards; that’s a topic for another post. Open up the XIB file, and layout an interface like the one shown below.

IB UI Layout

UI Layout

If you need help building the interface, check out this post, or get in touch through the comments or email.

All UI controls are at the default settings except for the segmented control at the top, which has a third section. Make the following connections:

  • Outlet: Segmented Control as segments
  • Outlet: Progress view as progressBar
  • Outlet: Switch as cSwitch
  • Outlet: Activity Indicator as spinner
  • Outlet: Text field as textField
  • Outlet: “Start spinning” button as spinningButton
  • Outlet: Three sliders as slider1, slider2, slider3
  • Outlet: Text view as textBox
  • Action: spinningButton as toggleSpinner:
  • Action: Segmented control, switch, text field, and sliders as controlValueChanged:
Actions and Outlets Connection

Actions and Outlets Connection

Also, make CCViewController.h the delegate of the text view. Again, if you need help, get in touch through the comments or email. You can also download the project at the end of this post.

Implementing the Code

We have three methods to implement—one to toggle the spinner, one to save the state and value of most of our controls, and the delegate for the text view, where we will save the text view’s contents. The first one is easy:

- (IBAction)toggleSpinner:(id)sender {
	if (self.spinner.isAnimating) {
		[self.spinner stopAnimating];
		((UIButton *)sender).titleLabel.text = @"Start spinning";
	}
	else {
		[self.spinner startAnimating];
		((UIButton *)sender).titleLabel.text = @"Stop spinning";
	}
}

Here, we simply check to see if the spinner is spinning (animating), and toggle it the other way. We also update the button label to reflect its new action.

User Defaults

The next method is where most of the persistence work play in. We’ll check the sender argument, and save its value to an appropriate store location.

- (IBAction)controlValueChanged:(id)sender {
	if (sender == self.segments)
		// Something
	else if (sender == self.cSwitch)
		// Something
	else if (sender == self.textField)
		// Something
	else if (sender == slider1)
		// Something
	else if (sender == slider2)
		// Something
	else if (sender == slider3)
		// Something
	else
		return;
}

In the first instance, we’re going to save the selected segment into NSUserDefaults. NSUserDefaults allows you to store basic configuration information in key-value pairs. Per Apple’s documentation:

The defaults system allows an application to customize its behavior to match a user’s preferences. For example, you can allow users to determine what units of measurement your application displays or how often documents are automatically saved. Applications record such preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults since they’re commonly used to determine an application’s default state at startup or the way it acts by default.

The code looks like this:

if (sender == self.segments) {
		int selectedSegment = ((UISegmentedControl *)sender).selectedSegmentIndex;
		[[NSUserDefaults standardUserDefaults] setInteger:selectedSegment forKey:@"SelectedSegmentIndex"];
	}

NSUserDefaults provides support for scalar types, so you don’t have to box the integer into an NSNumber. Other NSUserDefault “setters” include:

-setBool:forKey:
-setFloat:forKey:
-setInteger:forKey:
-setObject:forKey:
-setDouble:forKey:
-setURL:forKey:

To remove a value by key, use -removeObjectForKey:.

Plist

Plists have been a standard way to store text and settings since the early days of Cocoa. Plist data can be either in XML or binary format, and it is possible to convert between the two formats. Although there are arguably better alternatives, plists are still a standard way of saving data in iOS and is baked into a number of Foundation classes for easy access. In this case, we’ll create a key-value dictionary and save its contents to a plist. This is the basic way of saving configuration information to plist.

Before we begin, let’s look at an example plist. You can find a number of them ~/Library/Preferences.

Plist preview

Plist preview

The plist appears to be in an XML format, but if you open the file in a text editor, you see the incomprehensible binary nature of the file.

Plist binary

Plist binary

In our case, we’ll let NSDictionary handle the data transfer. NSDictionary has a -writeToFile:atomically: method, which writes a “property list representation of the contents of the dictionary to a given path.” You can re-create the dictionary from the file using the initWithContentsOfFile: method.

Only certain object types can be written to plists. These include instances of NSData, NSDate, NSNumber, NSString, NSArray, and NSDictionary. -writeToFile:atomically: will check of all objects in the dictionary are of these types; if not, the method will return NO and the file will not be written.

To support writing this data, we need to create a mutable dictionary. Call it controlState, and add it to our controller. Then, we need to amend toggleSpinner: to save the spinner’s state to the dictionary:

- (IBAction)toggleSpinner:(id)sender {
	if (self.spinner.isAnimating) {
		[self.spinner stopAnimating];
		((UIButton *)sender).titleLabel.text = @"Start spinning";
		[self.controlState setValue:[NSNumber numberWithBool:NO] forKey:@"SpinnerAnimatingState"];
	}
	else {
		[self.spinner startAnimating];
		((UIButton *)sender).titleLabel.text = @"Stop spinning";
		[self.controlState setValue:[NSNumber numberWithBool:YES] forKey:@"SpinnerAnimatingState"];
	}
}

We’re just interacting with basic NSDictionary APIs. We do something similar in the second and third cases of controlValueChanged:

else if (sender == self.cSwitch)
		[self.controlState setValue:[NSNumber numberWithBool:self.cSwitch.enabled] forKey:@"SwitchEnabledState"];
	else if (sender == self.textField)
		[self.controlState setValue:self.textField.text forKey:@"TextFieldContents"];

Finally, we’ll save the dictionary at the end of controlValueChanged:.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectoryPath = [paths objectAtIndex:0];
	NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:@"componentState.plist"];
	[self.controlState writeToFile:filePath atomically:YES];

We get the documents directory, and create a file path by appending the filename to the directory string. We pass it into the NSDictionary method, and tell it to write to the file atomically (writing to a temp file then renaming it to prevent data damage during the writing process—you either have the old version or the new, but nothing corrupt, in-between).

Archiving Objects

Foundation provides a mechanism to save data objects as binary data through a process of encoding/decoding objects. The encoding operation supports scalar types and objects that support the NSCoding protocol. Most Foundation data types already support this protocol. If you want to encode your own data objects, you’ll want to implement NSCoding as well. NSCoding defines two methods, which in a data class might be implemented as follows (variables and properties are assumed to be declared already):

- (void)encodeWithCoder:(NSCoder *)encoder {
	[encoder encodeObject:obj1 forKey:@"obj1Key"];
	[encoder encodeInt:anInt forKey:@"IntValueKey"];
	[encoder encodeFloat:aFloat forKey:@"FloatValueKey"];
}

- (id)initWithCoder:(NSCoder *)decoder {
	if (!(self = [super init]))
		return nil;
	obj1 = [decoder decodeObjectForKey:@"obj1Key"];
	anInt = [decoder decodeObjectForKey:@"IntValueKey"];
	aFloat = [decoder decodeObjectForKey:@"FloatValueKey"];
}

If your class’s superclass also adopts NSCoding, you should call [super encodeWithCoder:encoder] and [super initWithCoder:decoder] in the respective methods. In our case, however, we are not defining a custom data class. All our archiving will be handled in the controller. Create a new NSMutableDictionary called sliderValues and add it as a property to the controller class. Now we add the slider values to the array as they change:

else if (sender == slider1)
		[self.sliderValues setValue:[NSNumber numberWithFloat:slider1.value] forKey:@"Slider1Key"];
	else if (sender == slider2)
		[self.sliderValues setValue:[NSNumber numberWithFloat:slider2.value] forKey:@"Slider2Key"];
	else if (sender == slider3)
		[self.sliderValues setValue:[NSNumber numberWithFloat:slider3.value] forKey:@"Slider3Key"];

Here’s how we encode the object (at the end of controlValueChanged:):

NSMutableData *data = [NSMutableData data];
	NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
	[archiver encodeObject:self.sliderValues forKey:@"SliderValues"];
	[archiver finishEncoding];
	NSString *dataPath = [documentsDirectoryPath stringByAppendingPathComponent:@"archivedObjects"];
	[data writeToFile:dataPath atomically:YES];

The archiver object is created with an instance of NSMutableData, and when the archive process is finished the data object contains a binary representation of the original object. This data can then be saved out to a path.

Archiving objects is a really easy way to save relatively complex data objects to a file. However, the data is written in a binary format, and as such can only be used on OS X and iOS. Also note that a number of other classes, including NSArray, support writing to plist, and most Foundation data classes support data encoding.

Finally, we have to instantiate the dictionaries; otherwise we’ll be writing to a null pointer:

- (void)viewDidLoad {
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
	self.controlState = [NSMutableDictionary dictionary];
	self.sliderValues = [NSMutableDictionary dictionary];
}

Text File

It is relatively easy to save an NSString to a text file. Text files are human readable and can be made into an efficient cross-platform data source. We’re going to save the contents of the text field to a string, To do that, we have to implement a UITextViewDelegate method. Here’s the code:

- (void)textViewDidChange:(UITextView *)textView {
	NSString *textViewContents = textView.text;
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectoryPath = [paths objectAtIndex:0];
	NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:@"TextViewContents.txt"];
	[textViewContents writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

The method of interest is writeToFile:atomically:encoding:error: on an instance of NSString. The first argument is a file path, the second is a boolean, the third is one of a few constants (most of the time you’ll use NSUTF8StringEncoding), and you can pass a pointer to an error if you want to know if anything went wrong.

Restoring Data

We’ll restore all the data to the controls in viewWillAppear:. The APIs are pretty straightforward.

- (void)viewWillAppear:(BOOL)animated {
	[super viewWillAppear:animated];
	
	// Load segmented control selection
	int selectedSegmentIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"SelectedSegmentIndex"];
	self.segments.selectedSegmentIndex = selectedSegmentIndex;
	
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectoryPath = [paths objectAtIndex:0];
	
	// Load data from plist
	if ([[self.controlState allKeys] count] == 0) {
		NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:@"componentState.plist"];
		self.controlState = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
		
		if ([[self.controlState objectForKey:@"SpinnerAnimatingState"] boolValue])
			[self.spinner startAnimating];
		else
			[self.spinner stopAnimating];
		
		self.cSwitch.enabled = [[self.controlState objectForKey:@"SwitchEnabledState"] boolValue];
		
		self.progressBar.progress = [[self.controlState objectForKey:@"ProgressBarProgress"] floatValue];
		
		self.textField.text = [self.controlState objectForKey:@"TextFieldContents"];
	}
	
	// Decode objects
	if ([[self.sliderValues allKeys] count] == 0) {
		NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:[documentsDirectoryPath stringByAppendingPathComponent:@"archivedObjects"]];
		NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
		self.sliderValues = [decoder decodeObjectForKey:@"SliderValues"];
		self.slider1.value = [[self.sliderValues objectForKey:@"Slider1Key"] floatValue];
		self.slider2.value = [[self.sliderValues objectForKey:@"Slider2Key"] floatValue];
		self.slider3.value = [[self.sliderValues objectForKey:@"Slider3Key"] floatValue];
	}
	
	// Read text file
	NSString *textViewData = [NSString stringWithContentsOfFile:[documentsDirectoryPath stringByAppendingPathComponent:@"TextViewContents.txt"] encoding:NSUTF8StringEncoding error:nil];
	self.textBox.text = textViewData;
}

First, we read values out of NSUserDefaults to restore the selected segment. Next, we get the documents path, then load the plist array using an NSDictionary method. We then read the values in the dictionary through the key and set the proper values on our controls. We do the same thing with the sliders, except that we have NSKeyedUnarchiver decode the binary data into a dictionary. Finally, we read in a text file using an NSString method and set the text inside the UITextView to that string.

That’s all there is to basic data persistence. When you quit and relaunch the app, all the settings and control values will still be there.

Download the project here.

The Jungle, Part 7: Quartz Demos (Section 3 of 3)


In this last section, we will combine the drawing abilities of Quartz with the blazing fast animations that are provided by Core Animation.

Core Animation Primer

Core Animation is a framework for animating a number of properties on views. It was introduced with OS X 10.5 (Leopard) and iPhone OS 3.0. Animation is important because it conveys visual feedback , especially in state change. For example, zooming in on OS X and iOS is an animated process, rather than jumping from one zoom level to another. This shows what happened, rather than providing a visual disconnect. Core Animation handles the animation implicitly, which means that, if you choose to accept the default options, you can simply set a property and the transition will be animated. Of course, you can also have fine-grained control of the animation.

Core Animation animations are fully GPU-backed and coded through OpenGL. This allows the animations to be incredibly fast—the original iPhone could hit 30fps on UI animations while Windows XP and earlier versions of Android performed all the animation code in the CPU, which is a significant performance bottleneck (the benefits of hardware accelerated graphics).

Core Animation exists as a backing layer behind your views. The layer is a cached copy of the view stored in the graphics card. It propagates down a view hierarchy—subviews of a view are automatically backed, but parent views are not automatically backed. It might be easiest to back the topmost view, but because each layer is stored in video memory (which may be shared with the main system memory), it is best to minimize your memory footprint. Back only the layers you need to animate.

Core Animation allows you to perform some styling options that are much simpler than using Quartz. For example, you can take an image, apply a styled border, round the corners, and put a drop shadow underneath—and animate all of it, using a few lines of Core Animation code.

Let’s get started.

General-Purpose Drawing with Core Animation

Open up CustomView.m in our sample project, and go to drawOtherInContext:. First we’ll look at how to draw a rounded rectangle in Quartz. This uses a method introduced with the iPad in iOS 3.2; it was not available on the iPhone until iOS 4.2, seven months later; Core Animation was introduced in iOS 3.0.

First, make sure to import QuartzCore.h in CustomView.h:

#import <QuartzCore/QuartzCore.h>

The Quartz code:

UIBezierPath *roundedRectQuartz = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 70, 90) cornerRadius:8.0];
	[[UIColor orangeColor] setFill];
	[roundedRectQuartz fill];

The Core Animation code:

UIView *roundedRectView = [[UIView alloc] initWithFrame:CGRectMake(90, 10, 70, 90)];
	roundedRectView.backgroundColor = [UIColor orangeColor];
	roundedRectView.layer.cornerRadius = 8.0;
	[self addSubview:roundedRectView];

Although the Core Animation code is actually a line longer, you get to work with standard UIKit interfaces; in fact, you could perform CA-type drawing on existing UIView elements (like buttons and text fields) without having to subclass them (remember that Quartz runs through the drawRect: method, so you’d have to subclass to use Quartz). The line of interest is the third one, where we access the layer property on the newly created view. This layer is a reference to a CALayer, the class the represents the CA backing layer. The cornerRadius is a built-in property of the class.

You can manipulate CALayers as you would UIViews. So you can create another layer and add it to your existing layer:

CALayer *shadowBox = [CALayer layer];
	shadowBox.backgroundColor = [UIColor purpleColor].CGColor;
	shadowBox.shadowColor = [UIColor blackColor].CGColor;
	shadowBox.shadowRadius = 1.0;
	shadowBox.shadowOpacity = 0.3;
	shadowBox.shadowOffset = CGSizeMake(1.0, -2.0);
	shadowBox.frame = CGRectMake(120, 30, 20, 30);
	[self.layer addSublayer:shadowBox];

The results are exactly what you expect, but using easy Objective-C rather than C.

Finally, we’ll play around with an image:

CALayer *imageBox = [CALayer layer];
	imageBox.backgroundColor = [UIColor blackColor].CGColor;
	imageBox.borderColor = [UIColor whiteColor].CGColor;
	imageBox.borderWidth = 3.0;
	imageBox.cornerRadius = 10.0;
	imageBox.shadowColor = [UIColor blackColor].CGColor;
	imageBox.shadowRadius = 3.0;
	imageBox.shadowOpacity = 0.8;
	imageBox.shadowOffset = CGSizeMake(2.0, 2.0);
	imageBox.frame = CGRectMake(180, 10, 102, 64);
	CALayer *imageLayer = [CALayer layer];
	imageLayer.contents = (id)[UIImage imageNamed:@"Image Fill.jpg"].CGImage;
	imageLayer.cornerRadius = 10.0;
	imageLayer.masksToBounds = YES;
	imageLayer.frame = imageBox.bounds;
	[imageBox addSublayer:imageLayer];
	[self.layer addSublayer:imageBox];

Here, we actually need to create two layers. To force the image to have rounded corners (by default it’ll draw the image regardless of the corners), you need to set the masksToBounds property to YES. This, however, prevents the shadow from being drawn, as the shadow is outside of the bounds. Therefore, you need a second layer to hold the image; the first will contain the border and shadow.

you can also perform Quartz-like custom drawing with CALayers as well. You need to set a delegate for the layer; the delegate must implement drawLayer:inContext:, which is analogous to drawRect:. You then call setNeedsDisplay on the layer, which works just as it does with UIViews.

Animating with Core Animation

Let’s look at a quick example:

CALayer *pulsingBox = [CALayer layer];
	pulsingBox.backgroundColor = [UIColor whiteColor].CGColor;
	pulsingBox.borderColor = [UIColor blackColor].CGColor;
	pulsingBox.borderWidth = 2.0;
	pulsingBox.cornerRadius = 5.0;
	pulsingBox.frame = CGRectMake(10, 10, 80, 50);
	CABasicAnimation *pulsingAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
	pulsingAnimation.toValue = (__bridge id)([UIColor orangeColor].CGColor);
	pulsingAnimation.duration = 3;
	pulsingAnimation.repeatCount = 10;
	pulsingAnimation.autoreverses = YES;
	[pulsingBox addAnimation:pulsingAnimation forKey:@"backgroundColorPulse"];
	[self.layer addSublayer:pulsingBox];

Here we create a CALayer as before. Then we create an instance of CABasicAnimation, which allows us to animate the value of a key path. We want this to pulse, so we only need to set an ending value; setting a starting value is usually redundant anyway. The (bridge id) bit is simply an ARC-specific cast of a struct type to id. We set the duration of the animation, a repeat count, and have it automatically reverse, which gives the pulse we’re looking for. We then add the animation to the layer, which implicitly causes it to start animating. The addAnimation:forKey: method takes a string as its second argument; this is the string that is used to identify the animation. To stop the animation before it’s finished, you call removeAnimationForKey:, using the same key. You can also send removeAllAnimations to stop all animations for a layer.

In this way, you can only animate one property at a time. You can combine multiple CAAnimation objects in a CAAnimationGroup object, which contains an array of CAAnimations. You then set the animation group as the animation on a layer, and all the properties animate. This is useful for setting the timing on a group; the timing of animations within the group are clipped to the timing of the group.

Motion Paths and Repetition

You can create much more complicated animations using keyframes. Keyframes are locations in the animation where you explicitely set the values of certain parameters, and the animation system will calculate all the intermediate steps based on the animation properties and the start and end values. In Core Animation, this is represented by CAKeyframeAnimation.

CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath: @"position"];
bounceAnimation.removedOnCompletion = YES;
bounceAnimation.fillMode = kCAFillModeForwards;
bounceAnimation.duration = 5;
bounceAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

We create our animation, tell it to remove itself after it’s done (to reduce processing and memory usage), retain the final position after it’s done (that’s what the fillMode specifies), make it take 5 seconds, and accelerate at the start and decelerate at the end.

Next, we have to define the path for the animation to follow (since we are animating the position). We create a mutable path object and draw to that, just as we would to a context. However, the functions we use have “path” in their names, rather than “context”. Finally, we assign the path to the animation.

CGMutablePathRef bouncePath = CGPathCreateMutable();
	CGPathMoveToPoint(bouncePath, NULL, 0, 120);
	CGPathAddArc(bouncePath, NULL, 0, 180, 60, 0.5*M_PI, 0, 0);
	CGPathAddArc(bouncePath, NULL, 120, 180, 60, M_PI, 0, 0);
	CGPathAddArc(bouncePath, NULL, 240, 180, 60, M_PI, 0, 0);
* 	CGPathAddArc(bouncePath, NULL, 360, 180, 60, M_PI, 0, 0);
	[bounceAnimation setPath:bouncePath];

Finally, we create a view to animate, and add the animation to the view.

UIView *animatingView = [[UIView alloc] initWithFrame:CGRectMake(0, 90, 48, 60)];
	animatingView.backgroundColor = [UIColor redColor];
	[self addSubview:animatingView];
	[animatingView.layer addAnimation:bounceAnimation forKey:nil];

We’ve gotten the view to animate, following a crazy path that we’ve defined. You’ll note that it’s not very smooth…but that comes down to the timing function. You can adjust the timing function just as you could the position of colors in a gradient. But that’s a topic for another time.

Sorry for the lack of images in this post: I lost a lot of data when a power surge knocked out my computer and much of my backup as well. All the screenshots I had were lost. But here’s the code from this project, so you can build and run at your leisure.

Download here

The Jungle, Part 6: Navigation Controllers and Stacks


Navigation controllers are a cornerstone of iOS—they allow you to present a lot more information than you could fit on one screen, in a hierarchical format that is intuitive to the user. What does that mean? Think of it as a deck of cards (to use an oft-quoted metaphor). You have a stack of views with a “vertical” order. You can only see the top view, but there can be views underneath, and you can put views on top. And nav controllers are everywhere, so users are already familiar with it.

A UINavigationController handles the nav stack, as well as the corresponding animations. All you have to do is push and pop views controllers, if necessary. You will usually have to push view controllers on the stack, but by default, the back arrow in the upper-left will pop the controller automatically. You do have the ability to pop any number of existing view controllers programmatically if you need to—for example, in an app with a tab bar, tapping on a tab will take you to the root controller (first controller), which means that if you’re already in that tab, it’ll programmatically pop to the root controller.

Tab bar controllers and nav controllers are often seen together. In these situations you have some choices as to the design of the app. If each tab is displaying different content, you may want each tab to contain its own nav controller as the tab’s assigned view controller. Alternatively, if your different tabs just show a different sorting order or a different view on the same data, you might want to have the tab bar exist independently of the nav controller, and simply refresh the nav controller’s view when a different tab is selected. It is a bad idea to have each nav controller own its tab bar; you should not change your tab bar across different views of your app.

We’ll add navigation to our demo app, so open up the app that we’ve been working on not too long ago. Create a new UIViewController and call it CustomDrawingViewController (yes, next post we’ll look at doing some custom drawing using Quartz/Core Graphics—same thing). We’ll add a property that will allow you to set the title of the view before you push it on. Declare and synthesize a property of type NSString called viewTitle. Assign that property as the title in viewDidLoad:

if (!self.viewTitle)
		self.title = @"Custom Drawing";
	else
		self.title = self.viewTitle;

If the property did not get set, it’ll use the generic “Custom Drawing” title; otherwise it uses whatever was set. Now go to GraphicsTableViewController.m, where we’ll push the view controller. We do that in tableView:didSelectRowAtIndexPath:. Replace the deselect method with the following:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    CustomDrawingViewController *drawingVC = [[CustomDrawingViewController alloc] init];
	drawingVC.viewTitle = [[tableView cellForRowAtIndexPath:indexPath] textLabel].text;
	[self.navigationController pushViewController:drawingVC animated:YES];
}

Import CustomDrawingViewController.h. In this code, we create an instance of our new view controller. We set the title to be the text of the table view cell that was just tapped. Finally, we push on the new view controller. Note the call to self.navigationController. Nav controllers are so common in iOS that UIViewController contains a property to a parent navigation controller. If the view controller is not a child of a nav controller, then the property will be nil. Also note the animated parameter in the last method. You always want to animate the change if it’s going to be visible. Put another way, the only time you don’t want to animate is if you’re loading the first view of a stack, because the animation won’t be seen anyway.

Build and run, and select the Graphics Demo tab. Tap on any cell, and you’ll see an animation to a blank screen.

Nav Controller

Pushing on a new view

There will also be a back arrow in the upper left. Tap on that to go back.

Download the current version here.

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.

Design Patterns: Key-Value Observing


Key-Value Observing (KVO) is heavily used in OS X development in bindings and form a significant portion of UI design. However, with iOS, it takes on somewhat less significance and is mostly used as necessary to simplify program design. KVO “broadcasts” a notification whenever a property is modified, and any class can receive those notifications and handle changes as necessary. This helps to decouple your classes from each other, allowing more flexible and re-usable code, especially as your code grows large and you have many sets of MVC objects.

KVO Layout

KVO Structure

It is primarily used when you have one model object backing multiple controllers, in which case you can use KVO to update the model when any controller changes its data.

Preparing for Key-Value Observing

KVO is set up for you when you use properties. Whenever you use dot-notation or the setter methods to change a property, the corresponding KVO method is called and all receivers will be notified. If you do not use properties or write your own setters, you may want to manually call change notification methods; see the section below to do so.

Key-Value Observing Compliance

To be KVO-compliant, your class must first be KVC compliant. This means that you should have proper, working implementations for valueForKey: and setValue:forKey:. In addition, you should emit the proper notifications when changes are made.

As stated above, if you use standard setters, the notifications are called for you, and you don’t have to worry about them.

Manually Call Change Notifications

You may need to manually call notifications if you don’t use standard setters, if you want to minimize notifications for certain situations, or to package multiple changes into a single notification.

First, you have to override the implementation of automaticallyNotifiesObserversForKey:, which is a class method declared in the NSKeyValueObserving protocol. Given a key, if you want manual notification, return NO; otherwise, return YES. If the key is not recognized, call the super implementation. The default implementation in NSObject simply returns YES.

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { if ([theKey isEqualToString:@"openingBalance"]) return NO; else return [super automaticallyNotifiesObserversForKey:theKey]; }

Then, to call the actual notifications, you call willChangeValueForKey: before changing the value, and didChangeValueForKey: after the change.

- (void)setBalance:(double)newBalance { // Should not compare double directly, // But means the same as if (balance == newBalance) if (fabs(balance - newBalance) < 0.0001) return; [self willChangeValueForKey:@"balance"]; balance = newBalance; [self didChangeValueForKey:@"balance"]; }

Note that to minimize redundant notifications, you can check to see if the value has actually changed.

Finally, you can call willChangeValueForKey: and didChangeValueForKey: multiple times with different keys to send multiple notifications if multiple values get changed.

Registering for Key-Value Observing

To register a class for Key-Value Observing, the observed class must be KVO-compliant for the properties that you want to observe (obviously), you must register as an observer, and implement the observing method.

Note that not all classes are KVO-compliant for all properties. Ensure compatibility as necessary in your classes, but note that properties in Apple’s code are only KVO-compliant of the documentation says so.

Register an Observer

The observed object must be made aware of the observer by sending the addObserver:forKeyPath:options:context: method. In your view controller, you might have the following method, which registers a model object as an observer for the balance property:

- (void)registerObserver { [modelObject addObserver:self forKeyPath:@"balance" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; }

The options argument takes one or more of the NSKeyValueObservingOptions values. You will most often use the constants in the example above, which tells the system to return both the original and new values. You specify both using the bitwise OR operator (|), the vertical bar.

The context is a pointer to anything you wish, and is provided as-is to the observer. You can use it as an identifier to determine the change, or to provide any other data. It is your responsibility to retain and/or release it as necessary; it is not retained by the system. The context can be used to identify your notifications. A problem may arise because you can register the same keyPath on multiple objects. You can therefore use a distinguishing context to determine the calling class. See this link for more information on how to solve this issue.

Receiving Notifications

All observers must implement the observeValueForKeyPath:ofObject:change:context: method. The observer is provided the original object and key path that triggered the change, a dictionary containing the details of the actual change, and the user-specified context described above.

The dictionary has a value accessed through the NSKeyValueChangeKindKey, which is a standard NSDictionary change key. It provides information about the change, returning an NSKeyValueChangeSetting. The exact values are defined in the link. You can also access the old or new values (depending on whether neither, one, or both were requested) through the NSKeyValueChangeOldKey and NSKeyValueChangeNewKey keys. If the property is an object, it is returned directly; scalars or C structs are wrapped as an NSValue object.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"balance"]) { // Handle change } else // Unrecognized keypath [super observeValueForKeyPath:keyPath]; }

Removing an Observer

To clean up after yourself, you should remove yourself as an observer by calling the removeObserver:forKeyPath: method.

- (void)unregisterForKVO { [modelObject removeObserver:self forKeyPath:@"balance"]; }

If you received a context from the notifications, you should only release it after removing the observer.

Extension: Cocoa Naming Conventions


The Cocoa framework and applications that use Cocoa (Touch) follow a fixed set of naming conventions that make the programs easier to read, use, and understand.

General Principles

  • Keep your code brief, but never at the expense of clarity. You’ll often see long method names in Cocoa—some programmers will complain about them, but the truth is that they make the code that much more understandable. And with Xcode’s code completion, you really don’t have to worry about all that extra typing. As an example, consider the method name removeObjectAtIndex:. That’s a very clear method name—you’re removing an object, and the argument takes an index. However, the method remove: is significantly less clear. Are you passing in an object to be removed, or an index to be removed? Or something else? The method never specifies.
  • Don’t add redundancy to names. Prefixes such as “my” or suffixes such as “object” are (almost, in the case of the latter) always unnecessary—it is obvious that it’s your variable, or that (in many cases) it’s an object.
  • 95% of the time, don’t abbreviate unless it is a well-known technical term, such as PDF or URL. Abbreviations are often not as obvious as you think they are, especially for developers from other lingual backgrounds.
  • Avoid noun/adjective-verb dualities. For instance, you may encounter a method called displayText. Does that method perform the action of displaying the text, or does it signify the text that is displayed (versus some private identifier text)? The developer has no way of knowing.
  • Consistency is important. Don’t have a count method in one class and a size method in another. Classes which have methods of the same name should do the same thing with those methods.
  • Prefixes are encouraged in Cocoa, and are used throughout Cocoa itself. Foundation and many desktop Cocoa classes begin with NS; UIKit classes begin with UI. Many developers who publish source code will prefix things with their initials, or some initialism of their company. Prefixes are especially important in determining who wrote the code, especially with generic names, such as the String class (which exists in languages such as Java).
  • Class names should have a noun involved. Other names, especially enumerated types/state representations, should have some resemblance to the class name. For example, UITableViewCell is a class, and you can specify a style using constants such as UITableViewCellStyleDefault or UITableViewCellStyleSubtitle. These constants are long, but make it clear that they work with UITableViewCell and involve its style. If you just had Default and Subtitle, they’re very ambiguous, could be used by other classes (which could lead to interesting mix-ups and collisions), and are not at all clear about what they’re referring to.
  • Categories are usually expressed as className+categoryName, such as the fictitious NSString+HTMLFormatting. Note there the “HTML” abbreviation, which is completely acceptable because HTML is widely understood.
  • Delegate and Data Source protocols will have the descriptor in the name—note UITableViewDelegate and UITableViewDataSource.
  • Other protocol names usually take a verb form of the noun they modify. For example, the NSCoding protocol works with NSCoder and NSDecoder classes.
  • Method Names

  • Method names usually begin with a lowercase letter, and each subsequent “word” in the name has its initial letter capitalized. Avoid abbreviations that are not clear. Most methods should not begin with a prefix, but you may use a prefix to denote a private method. You may also use a leading underscore (no, Apple does not reserve this method, although any Apple method with a leading underscore is supposed to be private).
  • Methods that perform an action should start with a verb. Auxiliary verbs such as “do” are not necessary and add nothing to the method except extra typing. According to the documentation, “never use adverbs or adjectives before the verb.”
  • If the method returns an attribute, the method should have the same name as the attribute, without any other modifiers, such as “calculate” or “get”. This is the naming convention that getters for properties follow.
  • Name all arguments, and make the name describe the argument. See the remove: example above.
  • Only use “and” to link two parts of a method that do different thing. For example, do not write getValue:andData:, but a method name like getValue:andDelete: is correct because it is doing two things—reading in a value and deleting (potentially) something else.
  • Verbs should not be made into adjectives. performsCalculation is preferred to calculationPerformed
  • Delegate & Data Source Methods

  • The first argument should be the object that is making the delegate or data source call. In certain cases, a class such as a view controller may be managing more than one object, such as a table view. In that case, the view controller can check to see which table view is calling the method.
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        if (tableView == tableView1)
            // return 1;
        else if (tableView == tableView2)
            // return 2;
        else
            // return something else
    }

    You’re comparing instances here, so you can use the == operator. They’ll both point to the same thing, because you’re just passing pointers around—in memory, there is only one instance of tableView1 and tableView2.

  • Delegate methods often use “did” and “will” to notify the delegate that something is about to happen, or has happened. This allows the delegate to prepare or setup some data, or to clean up after an operation. The terms provide a sense of time.
  • Methods that ask a delegate for permission, such as shouldDeleteObjectAtIndex:, should use the term “should”.
  • Miscellaneous

  • Use enumerated types, often with a typedef, to declare constants, rather than plain integers. This gives a name to the numbers, and makes it much more meaningful later. UITableViewCellStyleDefault is a lot more meaningful than simply 0.
  • Preprocessor constants are usually in all-caps, with an underscore separating the words, LIKE_THIS. Preprocessor macros maybe be named either like this, or as a standard method name; the former is preferred. Preprocessor macros defined by the compiler have leading and trailing double underscores, such as __LINE__.
  • Exceptions are named as Prefix+UniqueName+Exception, such as NSIllegalSelectorException or NSRangeException.
  • A much more in-depth look at naming conventions, along with more examples and more rules is available from Apple’s developer documentation:

    Coding Guidelines for Cocoa.

Handy Little Tool for Errors


During the development of any application, there will be times when you have to track errors in your code. Having the errors be clearly expressed in the Console or in an alert will help with the debugging. As an example, rather than letting your application crash with an exception, it might make sense to log a clear error and defer the fix to a later time.

Because of this, I’ve made a simple little macro that formats the error in a nate way. This is the macro:

#define LOCATION_AS_NSSTRING [NSString stringWithFormat:@"Function: %s\nLine: %d\nFile: %s", __func__, __LINE__, __FILE__]
#define STRING_REPRESENTATION_OF_ERROR(x) [NSString stringWithFormat:@"\n==== ERROR ====\nError %d: %@\nSeverity: %@\n%@", x, x ## _STRING, x ## _SEVERITY, LOCATION_AS_NSSTRING]

It is actually two macros; the former is a “helper” macro and you interact with the latter. The macro works with three other preprocessor declarations. It’ll make the most sense to put this in context:

/******* Severity Levels ******/
#define SEVERITY_NO_ACTION @"No action required"
#define SEVERITY_LOW @"Low"	// Minimal user action to fix OR cannot fix, but doesn't severly affect usability
#define SEVERITY_HIGH @"High"	// Extensive user action to fix OR cannot fix, and affects usability somewhat
#define SEVERITY_CRITICAL @"Critical"	// Crashers; cannot recover from issue
#define SEVERITY_UNKNOWN @"Unknown"	// Not enough information to decide

#define ERROR_CODE_1 1	// No sections defined for library
#define ERROR_CODE_1_STRING @"No sections defined for library browser. Returning 1"
#define ERROR_CODE_1_SEVERITY SEVERITY_NO_ACTION

// … Some code later …
NSLog(@"%@", STRING_REPRESENTATION_OF_ERROR(ERROR_CODE_1));

This code example will output the following:

==== ERROR ====
Error 1: No sections defined for library browser. Returning 1
Severity: No action required
Function: <ContainingMethod>
Line: <SomeLineNumberOfCalling>
File: <ContainingFile>

So how does this work? It’s actually rather simple. You pass in the error code listing, and the preprocessor “assigns” this value to the “variable” x. Then it uses that variable in the standard NSString method call. It first “plugs in” the value of x, then combines the full token (remember that you pass in the entire token, ERROR_CODE_1, so that gets combined and forms ERROR_CODE_1 ## _STRING, which gets joined into ERROR_CODE_1_STRING). It then does the same thing with _SEVERITY token, resulting in ERROR_CODE_1_SEVERITY. Finally, it plugs in the results from the other macro, LOCATION_AS_NSSTRING, which in turn calls three built-in macros (__func__, __LINE__, and __FILE__), which return the calling function, line, and file is C-style strings.

So really, it’s quite simple. There are just a lot of macros involved, and it really becomes quite useful. As an example, you could use this to show a meaningful error to users with an alert, if necessary.

Advertisements
  • 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 223 other followers

  • Back to the Past

    November 2017
    S M T W T F S
    « Apr    
     1234
    567891011
    12131415161718
    19202122232425
    2627282930  
  • Time Machine

  • You count!

    • 620,617 views
  • Worldwide Stats

    free counters
%d bloggers like this: