Make a game in a few weeks


Hey guys,

My friends at MakeGamesWithUs are creating a great opportunity to improve your iOS skills this summer. The MakeGamesWithUs Summer Academy is an 8 week program where you learn how to design, code, and ship your own iPhone game with locations in New York City and the San Francisco Bay Area. Their curriculum is used at schools like MIT, UC Berkeley, and Carnegie Mellon and their alums have gone on to work at awesome tech companies, startups, and game studios. You can find out more information and apply here:www.mgw.us/academy. The deadline to apply is this Saturday and you can use CupsOfCocoa as a discount code for 10% off!
Thanks again for visiting cupsofcocoa.com!
Make an iOS game this summer! Apply with discount code CupsOfCocoa

Status Update


I’m sorry it’s been so long since the last post. I’ve gotten involved with two startups, which are taking up all the time I have (more details coming later!)

I’ll keep creating new content, but if anyone is interested in writing for CupsOfCocoa, get in touch! I’d love to have another person creating awesome tutorials and explanations for iOS (and related) developers.

Cheers!

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

Creating New Files in Xcode 4.5


This is an update to a number of older posts which involved creating new files in Xcode by selecting a specific subclassing option. This has changed in recent versions of Xcode; this is the way to do it as of Xcode 4.5 (the latest at the time of writing).

  1. Go to File > New > File… (or press Command-N)
  2. On the left of the sheet that appears, select Cocoa Touch under iOS. On the right, select Objective-C class. Click Next
    Creating a New File—Select File Type

    Creating a New File—Select File Type

  3. For “Class”, specify the name of your class. “Subclass of” lets you select a superclass; this selection changes the options you see below (for example, “With XIB for user interface” appears only when you’re subclassing a view controller). You can also type in a name and make a subclass of one of your custom classes.
    Creating a New File—Specify a Superclass

    Creating a New File—Specify a Superclass

  4. Click Next, pick a save location, and click Create. You should see your files appear in the File Navigator.

The Jungle, Part 9: Core Data


Core Data

This post will explore the depths of the Core Data API. Core Data is a technology that manages a complex SQL database, and wraps the C function calls into an object-oriented framework that is much easier to use. This database structure allows you to store complex data objects, and a lot of it—the whole system is incredibly efficient, potentially handling millions of items with ease.

We’ll create a project that creates, stores and displays a list of random colors. We will look at building the object model and the equivalent classes that result, working with objects in the database, and displaying the results in a table view. Core Data is set up to work seamlessly with the UITableViewDataSource; the data structure is almost completely in place already.

Xcode empty project

Create an empty project in Xcode

We’ll begin by creating an Empty Application. On the next screen, make sure “Use Core Data” is checked.

Use Core Data checkbox

Use Core Data

The “Use Core Data” checkbox mainly does three things—it links in the Core Data framework, creates a data model file, and creates and initializes the Core Data stack. The stack describes the architecture of Core Data, so that’s a great a starting point.

Core Data Objects

Core Data Objects

The Core Data stack

Every Core Data project starts with the data model. In our app, it’s called “ColorsDemo.xcdatamodeld”. This file, which comes with its own editor mode, lets you define the data structure of your project, which is comprised of the classes as well as their associated properties, along with the relationships between the classes.

The persistent object store represents the stored data file on disk; you normally don’t interact with the object store.

The persistent store coordinator is your more direct access to the data store; it is the connection to the database. Big Core Data projects can have their data model spread over several data model files; each one corresponds to an object store. The persistent store coordinator coordinates the interactions with each store file on disk; there is only one coordinator per stack.

The managed object model represents the objects in the data store during the execution of the program. It contains information about the objects (“entities”) you defined in the data store; it gets an aggregate of all the entities across all the store files from the persistent store coordinator.

The managed object context is where a lot of the interaction with Core Data occurs. This is where objects are created, modified, and then committed to disk. Apple calls this a “scratch pad for managed objects”.

The Data Model

Open the data model file. The editor contains a list (currently empty) of Entities (objects), Fetch Requests (you send fetch requests to find stuff in the database), and Configurations (used when you want to deal with subsets of multiple data stores. This is beyond the scope of this post). Click the + (“Add Entity”) button at the bottom to add an entity. An item should appear in the list, under Entities, with its name highlighted. Change the name to “Color”.

Creating an entity in the editor

Creating an entity in the editor

In the middle pane of the editor, you’ll see an area labelled “Attributes”. Here is where you define the entity’s attributes, analogous to properties in regular objects. Click the plus button at the bottom left of the Attributes block to create a new attribute. It should be called red, and it should be of type Float (select from the popup menu that appears when you click on the default value, Undefined.

In the Attributes inspector, uncheck the Optional checkbox. “Optional” indicates that the attribute doesn’t have to have a value, but in our case it doesn’t make sense for our color not to have a red value. Therefore, it shouldn’t be Optional. You’ll also see “Indexed”, which means that the value should be added to the database index, which makes searching faster. However, we won’t be searching by a specific color value, so we can leave it out of the index. Adding values to the index will allow searches on those values to be faster, but a large index will slow down searches in general. Therefore, add only the values you actually intend to search. You’ll also see “Transient”, which signifies that the attribute is not actually stored to disk, but calculated in code. Core Data doesn’t make sure that the value actually is calculated; it is your responsibility to make sure that a transient attribute has a valid value when you try to access it.

You can also specify minimum, maximum, and default values for the attribute. For our red attribute, we set a minimum value of 0 and a max of 1. This range of values represents how UIColor accepts color values, and simplifies things. Core Data will enforce the limits and raise an exception if you try to store a value outside of this range. The default value is simply the value that the attribute gets created with. It’s not necessary to supply a default value, but it is a good idea to do so, especially for attributes that are not optional.

Attributes inspector

Attributes inspector

Create two more attributes with the same type and properties; call them green and blue. Also create an attribute of type String; call it name. Make sure it’s not Optional, that it is Indexed, and give it a default value of “Unnamed Color”. You can check a string by a minimum or maximum length, but we’re not going to impose length restrictions on our color names; you can also supply a regular expression to validate the input. Regular expressions (regexs) are beyond the scope of this post.

Now we’ve fully defined our Color entity in the editor. We can have Xcode generate the class template for us. Make sure the Color entity is selected, then go to Editor > Create NSManagedObject Subclass…:

Create NSManagedObject Subclass

Create NSManagedObject Subclass

Make sure “Use scalar properties for primitive data types” is checked. This setting creates ints and floats for properties rather than wrapping them up in NSNumber. This makes things faster, and because we’re simply using our values to create instances of UIColor, there’s no reason not to. Accept the default save location and click “Create”. You’ll see “Color.h” and “Color.m” appear in the File Inspector, and Xcode has generated property declarations for you already.

Xcode generates property declarations

Xcode generates property declarations

Toggle over to Color.m, and you’ll see something new: @dynamic. This is similar to @synthesize, except that the getter and setter methods aren’t generated by the compiler. In fact, @dynamic tells the compiler to assume that the methods are there and ignore the warnings that it would otherwise raise about the missing methods; Core Data inserts the appropriate methods in at runtime. However, you can still write your own getters and setters; Core Data will never override your code.

Add a readonly property to Color of type UIColor and call it derivedColor. This property is read-only because it is actually generated from the individual color components each time it is accessed. The getter looks like this:

- (UIColor *)derivedColor {
	return [UIColor colorWithRed:self.red green:self.green blue:self.blue alpha:1.0];
}

It would be more efficient to save a copy of the UIColor and only create a new one if the component colors were changed, but that’s a level of complexity that would distract from the main purpose of this post.

Building the UI

Now that we’ve defined the data structure, we can get around to building our UI. To do that, we’ll have to create a view controller and wire it up. In fact, for this project we’re going to create a navigation controller, providing room for a detail view controller which lets us explore more of what Core Data has to offer. Begin by pressing Command-N to create a new Objective-C class. Call it ColorsListViewController and make it a subclass of UITableViewController. Make sure you create the corresponding XIB as well.

Head over to AppDelegate.m. Import ColorsListViewController.h, and then in application:didFinishLaunchingWithOptions:, after the “Override point for customization” comment, add the following code to initialize our view controller, create the nav stack, and add it to the main window:

	ColorsListViewController *mainVC = [[ColorsListViewController alloc] init];
	UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:mainVC];
	self.window.rootViewController = navVC;

Next, we’re going to create a button that will create a new random color and add it to the table. Add the following code to viewDidLoad:

	UIBarButtonItem *plusButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewColor:)];
	self.navigationItem.rightBarButtonItem = plusButton;
Contacts Plus Button

Contacts Plus Button

We’re creating a standard plus button (like the one you see in the Contacts app), binding it to the addNewColor: method (which we’ll be defining in just a moment), and adding it to the right position in the nav bar.

Next, add the method declaration for addNewColor: to the header file and the following implementation:

- (IBAction)addNewColor:(id)sender {
	AppDelegate *ad = (AppDelegate *)[[UIApplication sharedApplication] delegate];
	NSManagedObjectContext *moc = ad.managedObjectContext;
	Color *newColor = (Color *)[NSEntityDescription insertNewObjectForEntityForName:@"Color" inManagedObjectContext:moc];
	newColor.red = [self randomColorComponentValue];
	newColor.green = [self randomColorComponentValue];
	newColor.blue = [self randomColorComponentValue];
	NSError *error;
	if (![moc save:&error]) {
		// Something's gone seriously wrong
		NSLog(@"Error saving new color: %@", [error localizedDescription]);
	}
	[self.colorsArray addObject:newColor];
	[self.tableView reloadData];
}

Make sure to import AppDelegate.h and Color.h. You’ll also need to declare colorsArray as a property of type NSMutableArray. We’re also using a helper method to generate a random number between 0 and 1. In this method, arc4random() returns a value between 0 and 2^32-1, so we divide the return value by 2^32-1 (equivalent to 100000000 in hexadecimal).

- (double)randomColorComponentValue {
	return ((double)arc4random() / 0x100000000);
}

Let’s take a look at what addNewColor: is doing. We get a reference to our app delegate because that’s where our managed object context is (Xcode’s Core Data template created the object for us. If you’re interested in where the object comes from, take a look at AppDelegate.m). We then get the managed object context itself. Next, we create a new instance of Color by adding a new object to the database. This is an important paradigm to understand. The insertNewObjectForEntityForName:inManagedObjectContext: method takes the name of the entity (our data model class) as its first argument, and the managed object context as its second. The method returns an object of type id; we cast it to an instance of Color. We then set the color components to a random value, and then save the color to our database using the managed object context. If save: fails, the method returns NO and our error will be set to something appropriate. It is a serious issue if the save fails; depending on the situation, the app should try again, ask the user to try again, or simply choose to crash if the data is unrecoverable and the app can’t continue without it (an extreme case). In this case, we just log the error. The rest of the method is simple: we also add the color object to the array that backs the table view, and then reload the table view.

Next, we’re going to populate our colorsArray in viewDidLoad so it contains data from whatever is already stored on disk. After alloc/initing the array, add the following code:

NSManagedObjectContext *moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"Color" inManagedObjectContext:moc];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    [request setSortDescriptors:sortDescriptors];
    // Fetch the records and handle an error
    NSError *error;
    self.colorsArray = [[moc executeFetchRequest:request error:&error] mutableCopy];
    if (!self.colorsArray) {
        // This is a serious error
		// Handle accordingly
		NSLog(@"Failed to load colors from disk");
    }

We begin by grabbing our managed object context again. Then we create an entity description, again passing in the name of our entity as a string. We then create a fetch request (a request to get stuff from the database) and set our entity description as the entity to fetch from the database. We then create a sort descriptor, which takes a key (the name of a property, the same type of key that key-value coding uses) and a sorting direction. Core Data handles the sorting logic for a variety of data types, including strings, numbers, and dates, but there are ways to custom sorting in a fetch request as well. We add our sort descriptor to an array (because our request wants an array), and set that on our fetch request. Then we tell our managed object context to perform the fetch request, and we create a mutable copy of the array that is returned, setting it to our colorsArray. If something goes wrong, we’ll have to handle the error accordingly.

Now it’s elementary to set up our table view data source methods:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	return [self.colorsArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *cellID = @"CellID";
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
	if (!cell) {
		cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
		cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
	}
	Color *currentColor = [self.colorsArray objectAtIndex:indexPath.row];
	cell.textLabel.text = currentColor.name;
	cell.detailTextLabel.text = [NSString stringWithFormat:@"Red: %.3f Green: %.3f Blue: %.3f", currentColor.red, currentColor.green, currentColor.blue];
	return cell;
}

There’s nothing new here; we’re just pulling values from our colorsArray and setting the text labels in our table view cell.

Build and run the app. You’ll be presented with a blank table view. Press the “+” button a couple of times, and new entries will appear in the table. Now, you can quit the app, and when you relaunch the entries will be right as you left them. That’s the persistent nature of Core Data at work.

A few entries saved with Core Data

A few entries saved with Core Data

Building the Detail View

We’ve played around with the basics of Core Data, but we can do more. We’re going to build a detail view that allows us to edit color names, see what our color looks like, and delete colors. Changes that we make will be written back to the database. Let’s get started by creating a subclass of UIViewController, call it DetailedColorViewController, and make sure to create the XIB as well.

Detailed view controller XIB layout

Detailed view controller XIB layout

The UI should look something like this. The first element is actually a borderless text field, with the text centered and set to size 24. In the middle is a generic UIView; we can change the background color to reflect the actual color that we’re representing. Finally, we have a button that will delete the color.

Declare these two methods in the header. Wire up the first one to the text field’s Did End on Exit event, and the second to the button’s Touch Up Inside event.

- (IBAction)didChangeColorName:(id)sender;
- (IBAction)deleteColor:(id)sender;

Also declare a property (strong, nonatomic) of type Color, called color, and import Color.h. Synthesize the property in the implementation file. The text field should also be hooked up to a corresponding property called nameField, and the basic view should be hooked up to a corresponding property called colorView. Make sure both are strong and nonatomic, and that both are IBOutlets.

A few lines of code are needed in viewDidLoad to set up the view.

	self.title = @"Inspect Color";
	self.nameField.text = self.color.name;
	self.colorView.backgroundColor = self.color.derivedColor;

Next, we’re going to implement the delete method. Again, the managed object context is our point of interaction, and the code is pretty simple. Ideally, you’d like to prompt the user with an action sheet to confirm the delete action, but for simplicity’s sake we’re simply going to go ahead with the delete here.

- (IBAction)deleteColor:(id)sender {
	NSManagedObjectContext *moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
	[moc deleteObject:self.color];
	[moc save:nil];
	[self.navigationController popViewControllerAnimated:YES];
}

We first get the managed object context (make sure to import AppDelegate.h). Then we simply call deleteObject:, passing the Color object we’re referring to here. We save the changes to disk; for simplicity’s sake we won’t concern ourselves with any errors. Then, we pop to the list view because our detail view is now referring to a Color that no longer exists.

Updating a managed object is as simple as updating the properties you want, and then saving the changes through the managed object context. NSManagedObject associates each object with a managed object context, which makes Core Data interactions much simpler.

- (IBAction)didChangeColorName:(id)sender {
	self.color.name = self.nameField.text;
	[sender resignFirstResponder];
	NSManagedObjectContext *moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
	[moc save:nil];
}

A detail worth pointing out: The color’s name is set to the value stored in our nameField, because that’s the purpose of the name field. However, resignFirstResponder (which dismisses the keyboard) is called on the sender. This allows our method to be more modular; we could connect the method to other (or even multiple) UI elements and it would still behave properly.

Now all we have to do is support the detailed view controller in our list view controller. Head back over to ColorsListViewController.m. Import DetailedColorViewController.h and implement the relevant delegate method:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	DetailedColorViewController *dcvc = [[DetailedColorViewController alloc] init];
	dcvc.color = [self.colorsArray objectAtIndex:indexPath.row];
	[self.navigationController pushViewController:dcvc animated:YES];
}

In viewWillAppear, we’ll refresh our table view because changes could have been made in the detailed view controller.

- (void)viewWillAppear:(BOOL)animated {
	[super viewWillAppear:animated];
	[self.tableView reloadData];
}

Build and run the app. Try changing a color name, hitting Return on the keyboard and going back to the list view. The entry will update to reflect your new name. Try deleting an entry and going back to the list view. In fact, you’ll notice that we end up with an entry without a name, where all the color components are 0. This is because our colorsArray wasn’t updated with the change. It still holds three items, although one of them is now effectively nil. Quitting and restarting the app will fix the issue, but we can fix it in code as well. Simply move the code in viewDidLoad fetching from disk (from getting the managed object context to the error handling if colorsArray is nil) into a separate method called fetchColorsFromDisk, and call that method at the end of viewDidLoad, and right before reloading the table view in viewWillAppear:

Modified Core Data entries

Modified Core Data entries

As you can see, Core Data provides an easy way to save data to disk. There is some initial architecture to sort through (although the Core Data template helps a bunch—see AppDelegate.m for all the rest of the code), but Core Data makes it incredibly easy to propagate changes to disk. Most of our code was UI code, presenting the information on disk.

Core Data’s real power comes from its speed and searching ability. You can handle databases of thousands of objects without perceived lag, and you have very powerful search tools at your disposal. Core Data also provides a way for you to migrate to future data model versions if you ever decide to change the way the data is represented. There’s a lot more to Core Data, but hopefully this was a helpful introduction! There will be more posts on Core Data in the future.

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

Adding Frameworks to an Xcode Project


A lot of Xcode projects require you to add additional frameworks to link against. Here’s how:

  1. Select the main project listing in the left column.
    Select Project

    Select Project

  2. Select Build Phases from the tabs near the top.
    Build Phases Tab

    Build Phases Tab

  3. Click the ‘+’ button in the “Link Binary With Libraries” section (you may have to twist it open.
    Select Frameworks

    Select Frameworks

  4. Choose the framework(s) you want to add, and click the “Add” button.
    Add Frameworks

    Add Frameworks

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


This section will continue from where we left off last week. We’ll work with solid fills, gradient fills, and image and pattern fills. Open up the project from last week, and let’s get started. Navigate to CustomView.m

Single Color Fills

In the last section we filled our paths with solid fill colors. In those cases, we started with a color defined with UIColor. In some cases, however, you may want more control. Quartz’s underlying color structure is represented using a data type called CGColorRef (sometimes abbreviated to CGColor). You can create a UIColor with a CGColor and vice versa. UIColor has the

+colorWithCGColor:

method, and instances of UIColor have a .CGColor property. For example, to create a CGColor that represents a bright aqua color, we could use this code:

[UIColor colorWithRed:0 green:0.5 blue:1].CGColor;

To fill a rectangle with this color, we could use the following code:

CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0 green:0.5 blue:1 alpha:1].CGColor);
CGContextFillRect(context, CGRectMake(20, 30, 80, 100));

Remember the state-based nature of Quartz—You set a color, or a certain style, then use it. We set a fill color, then use it to paint a rectangle using CGContextFillRect(). There is also CGContextStrokeRect().

That’s really all there is to single-color fills. You set a color, and then you fill a shape or path.

Gradient Fills

A gradient fill is a fill for a shape that transitions through two or more colors. Most gradient fills are linear, where the colors fade across the entire shape along a straight line, or radial, where they fade across a radius, and the colors form concentric rings. Less common is the circular gradient, where colors transition around a circle. We’ll look at the first two in this section.

Linear Gradient

A linear or axial gradient “varies along an axis between two defined end points. All points that lie on a line perpendicular to the axis have the same color value.” Let’s look at an example.

- (void)drawGradientFillsInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	CGFloat colors[] = { 
        1.0, 1.0, 1.0, 1.0, 
        0.0, 0.5, 1.0, 0.8
    };
	
    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
    CGColorSpaceRelease(baseSpace), baseSpace = NULL;
	
	CGRect rect = CGRectMake(50, 60, 100, 60);
    CGContextSaveGState(context);
    CGContextAddEllipseInRect(context, rect);
    CGContextClip(context);
	
    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
	
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGGradientRelease(gradient), gradient = NULL;
	
    CGContextRestoreGState(context);
	
    CGContextAddEllipseInRect(context, rect);
    CGContextDrawPath(context, kCGPathStroke);
	UIGraphicsPopContext();
}

We begin by defining the colors for our gradient. The colors are passed in as a C-style array of CGFloats consisting of color components. All colors are represented as four float values from 0.0 to 1.0, in the order Red-Green-Blue-Alpha. Here, we create a gradient that transitions from white to a partly-transparent version of the aqua color we saw above. We can create a gradient with more colors simply by adding more numbers to our colors array.

Next, we have to grab a color space, which basically is a representation of the color calibration. This is more useful when displaying the same thing across different color spaces, such as the difference between the screen and a printout. The next line is what we’re interested in—we get a Quartz gradient, of type CGGradientRef, by calling CGGradientCreateWithColorComponents(). This function takes four arguments. The first is the color space you got back. The second is an array of the component colors. This array should have as many items as the product of the fourth argument and the number of components the color space specifies. In this case, that would be 4. The third argument is the relative location of the colors in the gradient. Each CGFloat value must be between 0 and 1, and those values represent the location of the corresponding color in the gradient. For example, if you had four colors and you passed in [0, 0.1, 0.2, 1], the first three colors will be clustered at the start, 10% of the way along the gradient, and 20% of the way along the gradient. The last color would be at the end of the gradient. If you pass in NULL for this argument, as the code above does, the first color is assigned to location 0 (the start of the gradient), the last color is assigned to location 1 (the end of the gradient), and all other colors are equally spaced in between. The final argument is a count of the number of colors.

In our code example, after we create the gradient, we release the color space. Quartz has its own memory management system, because we’re not dealing with regular Objective-C objects.

In the next block of code, we create a rectangle to draw with. We then call CGContextSaveGState(). This is similar to pushing and popping the graphics context, except that each context has its own state of graphics states. By pushing a new graphics state, we constrain our gradient operations to only the shape we’re about to draw. We then draw the ellipse, and call CGContextClip() to clip or constrain the gradient to the outline of the shape.

Next, we set the start and end point of the gradient. This creates a line that the gradient follows; the location of the points determines the angle of the line, and consequently the angle of the gradient. In this case, we’re creating a vertical line down the center of the shape, from the top to the bottom. We then call CGContextDrawLinearGradient(), which takes five arguments. The first is the graphics context. The second is the CGGradient object we created earlier. The next two are the start and end points, and the last one is an integer that determines whether to draw the gradient’s end colors beyond the end points.

Here is another example:

CGFloat rainbowColors[] = {
		1.0, 0.0, 0.0, 1.0,
		1.0, 0.5, 0.0, 1.0,
		1.0, 1.0, 0.0, 1.0,
		0.0, 1.0, 0.0, 1.0,
		0.0, 1.0, 0.5, 1.0,
		0.0, 0.0, 1.0, 1.0,
		1.0, 0.0, 1.0, 1.0
	};
	CGFloat locations[] = {0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.85};
	CGGradientRef rainbow = CGGradientCreateWithColorComponents(baseSpace, rainbowColors, locations, 7);
	// CGColorSpaceRelease(baseSpace), baseSpace = NULL;
	CGRect square = CGRectMake(160, 20, 140, 140);
	CGContextSaveGState(context);
	CGContextAddRect(context, square);
	CGContextClip(context);
	startPoint = CGPointMake(160, 160);
	endPoint = CGPointMake(300, 20);
	CGContextDrawLinearGradient(context, rainbow, startPoint, endPoint, 0);
	CGGradientRelease(rainbow), rainbow = NULL;
	CGContextRestoreGState(context);
	CGContextAddRect(context, square);
	CGContextDrawPath(context, kCGPathStroke);

Here, we draw a diagonal rainbow.

Rainbow Gradient

Rainbow Gradient

Radial Gradient

A radial gradient “is a fill that varies radially along an axis between two defined ends, which typically are both circles. Points share the same color value if they lie on the circumference of a circle whose center point falls on the axis. The radius of the circular sections of the gradient are defined by the radii of the end circles; the radius of each intermediate circle varies linearly from one end to the other.” One of the ends may be a single point rather than a circle (a point is simply a circle with a radius of 0). If one circle is partly or completely outside the other, you will end up with a cone- or cylinder-like shape.

The code for drawing a radial gradient is similar to drawing a linear gradient. You define locations and color components and then create a CGGradientRef with a color space. You then call CGContextDrawRadialGradient() to draw the actual gradient. Let’s see an example:

CGFloat redBallColors[] = {
		1.0, 0.9, 0.9, 0.7,
		1.0, 0.0, 0.0, 0.8
	};
	CGFloat glossLocations[] = {0.05, 0.9};
	CGGradientRef ballGradient = CGGradientCreateWithColorComponents(baseSpace, redBallColors, glossLocations, 2);
	CGRect circleBounds = CGRectMake(20, 250, 100, 100);
	startPoint = CGPointMake(50, 270);
	endPoint = CGPointMake(70, 300);
	CGContextDrawRadialGradient(context, ballGradient, startPoint, 0, endPoint, 50, 0);
	CGContextAddEllipseInRect(context, circleBounds);
	CGContextDrawPath(context, kCGPathStroke);

This code will draw a glossy red ball:

Glossy Red Ball

Glossy Red Ball

In this code, we define the colors and gradient just as before. However, when drawing a radial gradient we don’t need to clip to a shape, unless we wanted to; in this case, we’re drawing a ball, so the default circular shape is fine for our needs. CGContextDrawRadialGradient() takes 7 arguments. The first two are the context and the gradient, same as before. The next two are the start point and the radius of the first circle—we’ll play with that next. The next two are the end point and the radius of the second circle; we set this at 50 to create a ball with a size of 100×100. The last argument is an integer specifying whether to draw beyond the bounds.

This example will draw a radial gradient background, clipped in a rectangle:

CGFloat backgroundColors[] = {
		0.3, 0.3, 0.3, 1.0,
		0.1, 0.1, 0.1, 1.0
	};
	CGGradientRef backgroundGradient = CGGradientCreateWithColorComponents(baseSpace, backgroundColors, NULL, 2);
	CGContextSaveGState(context);
	CGRect backgroundRect = CGRectMake(20, 150, 80, 50);
	CGContextAddRect(context, backgroundRect);
	CGContextClip(context);
	startPoint = CGPointMake(CGRectGetMidX(backgroundRect), CGRectGetMidY(backgroundRect));
	CGContextDrawRadialGradient(context, backgroundGradient, startPoint, 0, startPoint, 35, kCGGradientDrawsAfterEndLocation);
	CGContextRestoreGState(context);
	CGContextAddRect(context, backgroundRect);
	CGContextDrawPath(context, kCGPathStroke);

Here, we see a radial gradient constrained in a rectangle. The gradient’s radius doesn’t take it all the way to the edge of the rectangle, so the rest of the rectangle is filled with the end color. This is specified by the kCGGradientDrawsAfterEndLocation parameter passed to CGContextDrawRadialGradient().

Let’s look at one last example:

[[UIColor colorWithRed:0 green:0.5 blue:0 alpha:0.5] setStroke];
	CGContextAddEllipseInRect(context, CGRectMake(180, 180, 100, 100));
	CGContextDrawPath(context, kCGPathStroke);
	CGFloat coneColors[] = {
		0.2, 0.8, 0.2, 1.0,
		1.0, 1.0, 0.9, 0.9
	};
	CGGradientRef coneGradient = CGGradientCreateWithColorComponents(baseSpace, coneColors, NULL, 2);
	startPoint = CGPointMake(230, 230);
	endPoint = CGPointMake(280, 330);
	CGContextDrawRadialGradient(context, coneGradient, startPoint, 50, endPoint, 10, 0);
	CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.1 green:0.6 blue:0.1 alpha:0.3].CGColor);
	CGContextAddEllipseInRect(context, CGRectMake(270, 320, 20, 20));
	CGContextDrawPath(context, kCGPathStroke);
Quartz Cone

Quartz Cone

Because the circles are partly outside of each other, Quartz draws a cone figure.

Image and Pattern Fills

To make more complicated designs, sometimes it’s easier to use a pre-rendered image as a fill. You may also want to repeat (tile) it to fit the area, rather than stretching it and loosing quality.

We start by loading an image into a shape, and by clipping to the shape we can use it as a fill:

CGRect ellipseRect = CGRectMake(20, 30, 100, 80);
	CGContextSaveGState(context);
	CGContextAddEllipseInRect(context, ellipseRect);
	CGContextClip(context);
	[[UIImage imageNamed:@"Image Fill.jpg"] drawInRect:ellipseRect];
	CGContextRestoreGState(context);
	CGContextAddEllipseInRect(context, ellipseRect);
	CGContextDrawPath(context, kCGPathStroke);

This code is mostly old stuff. We have to clip to a shape to constrain the image to that shape; otherwise the whole image would be drawn in the entire rect. Larger images would in fact be drawn at full size, and could cover the whole screen and go beyond. We can draw the image using a method built into UIImage. -drawInRect: takes a CGRect as its only parameter, and draws into that rectangle, filling it unless a clipping path has been defined. There is a similar way to tile images:

CGRect tileRect = CGRectMake(150, 40, 150, 110);
	CGContextSaveGState(context);
	CGContextAddEllipseInRect(context, tileRect);
	CGContextClip(context);		// Clip; otherwise whole rect will be drawn
	[[UIImage imageNamed:@"TileImage.png"] drawAsPatternInRect:tileRect];
	CGContextRestoreGState(context);
	CGContextAddEllipseInRect(context, tileRect);
	CGContextDrawPath(context, kCGPathStroke);

This code should be self-explanatory. The results:

Quartz Pattern and Image  Fills

Quartz Pattern and Image Fills

In the next section, we’ll look at more advanced things we can do with Quartz and related frameworks, including Core Animation.

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


It’s been a while since we last plunged into the jungle. Previously, I’ve written about Quartz, which is the 2D graphics engine that powers the graphics in iOS. In this post, we’ll take a look at various examples of Quartz at work. I’ll explain everything as we go along. Open up the demo app from last time, and let’s get started!

Adding Frameworks

First, we have to add the CoreGraphics and QuartzCore frameworks to our project. These frameworks will support the Quartz code we’ll be writing.

Custom Views

In iOS, you draw to a view, not a view controller. This means that if you want a view with custom drawing, you’ll have to create a subclass of UIView (or a subclass of one of UIKit’s views, such as UITableViewCell), and if you have a view controller managing that view, then you may have to do the necessary configuration in code or Interface Builder as well. In our demo app, we see that we have a view controller called CustomDrawingViewController. Right now, it’s just managing a stock UIView. We’ll need to subclass UIView to do our own drawing, so create a new file, call it CustomView, and make it a subclass of UIView. Switch to CustomView.m and edit the initWithFrame: method:

- (id)initWithFrame:(CGRect)frame {
    if (!(self = [super initWithFrame:frame]))
		return nil;
	self.backgroundColor = [UIColor lightGrayColor];
	return self;
}

Here, we’re just setting a custom background for our drawing view. Head over to CustomDrawingViewController.m, and set our custom view as the view controller’s view:

- (void)loadView {
    // Implement loadView to create a view hierarchy programmatically, without using a nib.
	CustomView *customView = [[CustomView alloc] initWithFrame:CGRectMake(0, 44, 320, 480)];
	self.view = customView;
}

We use loadView here in place of a xib file, because (in my opinion) two lines of code, which is all we will need here, is simpler than a whole file to manage. Make sure you import CustomView.h. If you run the app now and browse through the graphics demos table, you’ll see our custom view with the gray background.

Drawing a Plan

We’re going to use one custom view to handle all our drawing needs. We will tell the view what type of content to draw, and then we can use Quartz subroutines (a fancy word for methods/functions) to draw the actual content. We’ll set up an enumerated type for the different demos we’ll work with. In addition, there is a preferred way to work with Quartz subroutines; we’ll discuss this as well.

Writing the Setup Code

We’ll begin by declaring some constants that we will reference throughout our app. In Xcode, create a new file (Command-N). Under “C and C++” on the left, choose “Header File” and save it as “Constants.h”. Add the following content:

typedef enum {
	QuartzContentStraightLines,
	QuartzContentCurves,
	QuartzContentShapes,
	QuartzContentSolidFills,
	QuartzContentGradientFills,
	QuartzContentImageFills,
	QuartzContentSimpleAnimations,
	QuartzContentBounce,
	QuartzContentOther,
} QuartzContentMode;

In CustomView.h, import Constants.h and declare the following property:

@property (assign, nonatomic) QuartzContentMode mode;

Synthesize the property. Also add the same property to CustomDrawingViewController and synthesize it.

In CustomDrawingViewController, import Constants.h and declare and implement the following method:

- (id)initWithContentMode:(QuartzContentMode)contentMode {
	if (!(self = [super init]))
		return nil;
	self.mode = contentMode;
	return self;
}

In loadView, after initializing customView, add the following line:

customView.mode = self.mode;

To use this new property, we have to set it when we select a row in our table. Go to GraphicsTableViewController.m, and replace the tableView:didSelectRowAtIndexPath: method with this:

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

Knowing that we have three rows in a section, we simply take the indexPath and use it to calculate an integer value that corresponds to our enumerated type (remember than enumerated types are basically specific names given to integer values) and pass it along.

In CustomView.m, add the following code:

#pragma mark - Drawing Methods
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
	switch (self.mode) {
		case QuartzContentStraightLines:
			[self drawStraightLinesInContext:context];
			break;
		case QuartzContentCurves:
			[self drawCurvesInContext:context];
			break;
		case QuartzContentShapes:
			[self drawCustomShapesInContext:context];
			break;
		case QuartzContentSolidFills:
			[self drawSolidFillsInContext:context];
			break;
		case QuartzContentGradientFills:
			[self drawGradientFillsInContext:context];
			break;
		case QuartzContentImageFills:
			[self drawImageAndPatternFillsInContext:context];
			break;
		case QuartzContentSimpleAnimations:
			[self drawSimpleAnimationsInContext:context];
			break;
		case QuartzContentBounce:
			[self drawBouncesInContext:context];
			break;
		case QuartzContentOther:
			[self drawOtherInContext:context];
			break;
		default:
			break;
	}
}

Quartz Methods

For each subroutine, we have to push and pop a graphics context. Quartz is a state-based drawing system. If we didn’t push on a graphics context, and we made a state change, such as changing the stroke or fill color, you don’t want to be caught off-guard when you return to the calling method. This could be a bigger issue if the subroutine you’re using was written by someone else; it would be nearly impossible (and very impractical) to restore the state to the way it was before the method call without pushing and popping a graphic context.

In the beginning of the subroutine, we have the following line:

UIGraphicsPushContext(context);/sourcecode]
Where "context" is an argument passed into the subroutine.
At the end of the method, pop the context:
UIGraphicsPopContext();

Set up all the subroutines as such:

- (void)drawStraightLinesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawCurvesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawCustomShapesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawSolidFillsInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawGradientFillsInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawImageAndPatternFillsInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawSimpleAnimationsInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawBouncesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

- (void)drawOtherInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	UIGraphicsPopContext();
}

Now let’s fill in those methods.

Drawing Lines

Drawing lines, or paths, as they are called in Quartz, begins with defining a path, moving and adding lines to points, and finishing the path. Then you can stroke and/or fill it to make it visible. Note that simply drawing and closing a path will make it exist in memory, but it will not actually be drawn on screen until you request a stroke or fill. We’ll start by drawing some lines and playing around with the settings.

- (void)drawStraightLinesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 35, 10);
	CGContextAddLineToPoint(context, 45, 65);
	[[UIColor blueColor] setStroke];
	CGContextStrokePath(context);
	UIGraphicsPopContext();
}

If you build and run and navigate to the corresponding view, you’ll see a thin blue line in the top-left of the screen.

First Quartz Path

First Quartz Path

To draw this line, we first have to begin the path using CGContextBeginPath(). This function (most of Quartz is written in C using C-style functions, rather than Objective-C methods) allocates memory for a path and prepares the drawing system, but doesn’t actually do anything visible. Think of it as uncapping a pen. The next function is CGContextMoveToPoint(), which takes as arguments the context and an x- and y- coordinate. This sets the beginning point of the line—think of it as moving a pen over the paper and putting it down at a point. Then, we call CGContextAddLineToPoint() which also takes the context and an x- and y- coordinate. This function is analogous to dragging a pen over the page to the second point. We then specify blue as the stroke color. Here, we’re using a convenience method that UIColor provides rather than using the corresponding Quartz call. The result is the same, and unless you’re working with custom colorspaces it is much easier to just use the convenience methods. There is a corresponding -setFill method for setting the fill color of a path. Finally, we call CGContextStrokePath() to actually draw the path in our blue color. By default the stroke width is 1.0 point. We can change that with a simple function call:

CGContextSetLineWidth(context, 5.0);

Add this line right after the UIColor call. Build and run, and you’ll see that the line is now much thicker.

Add the following line to the code, right after the last line:

CGContextSetLineCap(context, kCGLineCapRound);

If you build and run now, you’ll see that the line’s ends are not squared off, but nicely rounded. In fact, it is defined as such:

Quartz draws a circle with a diameter equal to the line width around the point where the two segments meet, producing a rounded corner. The enclosed area is filled in.

The idea here is that to draw lines in Quartz, you begin by defining the path and its endpoints. Then you specify the stroke color and other stroke settings, then actually stroke it. So try drawing another line—make it red, and stretch diagonally down and to the left from the upper-right corner.

Something like this:

CGContextBeginPath(context);
	CGContextMoveToPoint(context, 250, 35);
	CGContextAddLineToPoint(context, 85, 130);
	[[UIColor redColor] setStroke];
	CGContextSetLineWidth(context, 2.0);
	CGContextSetLineCap(context, kCGLineCapSquare);
	CGContextStrokePath(context);

You can simply append this code right after the existing code, but before popping the context. Here I’m using a square line cap, defined as:

Quartz extends the stroke beyond the endpoint of the path for a distance equal to half the line width. The extension is squared off.

One other aspect of paths is a dashed path, which “allows you to draw a segmented line along the stroked path”. The path can be varied in complexity. Here are a few examples:

CGContextBeginPath(context);
	CGContextMoveToPoint(context, 55, 120);
	CGContextAddLineToPoint(context, 65, 220);
	[[UIColor greenColor] setStroke];
	CGContextSetLineWidth(context, 3.0);
	float lengths[] = {2.0, 6.0};
	CGContextSetLineDash(context, 0, lengths, 2);
	CGContextStrokePath(context);
	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 105, 150);
	CGContextAddLineToPoint(context, 65, 290);
	[[UIColor blackColor] setStroke];
	CGContextSetLineWidth(context, 3.0);
	float lengths2[] = {7.5, 4.5, 1.0};
	CGContextSetLineDash(context, 3, lengths2, 3);
	CGContextStrokePath(context);
	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 180, 120);
	CGContextAddLineToPoint(context, 260, 340);
	[[UIColor orangeColor] setStroke];
	CGContextSetLineWidth(context, 2.0);
	float lengths3[] = {5.0, 3.0, 4.0, 2.0, 3.0, 5.0, 2.0, 4.0, 1.0, 8.0, 1.0, 2.0, 1.0, 3.0, 1.0, 4.0, 1.0, 5.0};
	CGContextSetLineDash(context, 2, lengths3, 18);
	CGContextSetLineCap(context, kCGLineCapRound);
	CGContextStrokePath(context);

The function to set the dash is CGContextSetLineDash(). This takes four parameters. The first is the context. The second is an amount of offset—you can start a few pixels into the pattern which you specify in the third argument, which is a C-style array of floats. The fourth argument is the number of elements in that array.

One final point here: we’ve made a lot of changes to the graphics context, setting stroke colors, line caps, and dash patterns. If we didn’t push and pop the context, these changes would be used for the rest of the drawing in drawRect:, which is almost never what we want. It’s important to push and pop because it restores the calling context.

Drawing Curves

Curve geometry is more complex than straight lines, but the same path concepts apply. There are two main types of curves you can draw: arcs, which are segments of a circle, and Bézier curves, which are free-form curves defined by tangent points. Arcs are much easier to work with. Let’s look at an example:

- (void)drawCurvesInContext:(CGContextRef)context {
	UIGraphicsPushContext(context);
	CGContextBeginPath(context);
	//CGContextMoveToPoint(context, 25, 50);
	//CGContextAddLineToPoint(context, 50, 25);
	CGContextAddArc(context, 120, 120, 40, 0.25*M_PI, 1.5*M_PI, 0);
	[[UIColor redColor] setStroke];
	CGContextStrokePath(context);
	UIGraphicsPopContext();
}

Build and run, and you should see a portion of a circle in red on the screen.

Quartz Arc

Quartz Arc

You can also lead into the arc with a straight line:

CGContextBeginPath(context);
	CGContextMoveToPoint(context, 25, 50);
	CGContextAddLineToPoint(context, 120, 25);
	CGContextAddArc(context, 120, 120, 40, 0.25*M_PI, 1.5*M_PI, 0);
	[[UIColor redColor] setStroke];
	CGContextStrokePath(context);

Quartz will then draw a straight line from the end of your line to the start of the arc.

You define an arc by calling the function CGContextAddArc(). The first argument is the context. The next two are x- and y- coordinates for the center of the arc—the center point of the circle from which the arc is drawn. The fourth argument is the radius of the circle. The next two are the start angle and end angle, measured in radians where zero is horizontally to the right—the “positive x-axis”. The last argument is either a 0 or 1, where 0 is counterclockwise and 1 is clockwise.

Bézier curves are usually quadratic or cubic in nature, and are defined by a mathematical formula that act on the starting and ending points and one or more control points. From Apple’s documentation on Quartz:

The placement of the two control points determines the geometry of the curve. If the control points are both above the starting and ending points, the curve arches upward. If the control points are both below the starting and ending points, the curve arches downward. If the second control point is closer to the current point (starting point) than the first control point, the curve crosses over itself, creating a loop.

The curve that results is always tangental to the path that can be drawn between the starting and ending points, tracing through all the control points in order.

The following example draws three curves, and shows the control path for one of them:

CGContextBeginPath(context);
	CGContextMoveToPoint(context, 150, 100);
	CGContextAddQuadCurveToPoint(context, 250, 20, 300, 100);
	[[UIColor purpleColor] setStroke];
	CGContextStrokePath(context);

	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 180, 220);
	CGContextAddQuadCurveToPoint(context, 300, 0, 310, 180);
	[[UIColor magentaColor] setStroke];
	CGContextStrokePath(context);

	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 10, 260);
	CGContextAddCurveToPoint(context, 100, 160, 210, 360, 300, 290);
	[[UIColor greenColor] setStroke];
	CGContextStrokePath(context);

	// Draw control path for cubic curve
	CGContextBeginPath(context);
	CGContextMoveToPoint(context, 10, 260);
	CGContextAddLineToPoint(context, 100, 160);
	CGContextAddLineToPoint(context, 210, 360);
	CGContextAddLineToPoint(context, 300, 290);
	[[UIColor darkGrayColor] setStroke];
	CGContextSetLineWidth(context, 0.5);
	float lengths[] = {2.0, 1.0};
	CGContextSetLineDash(context, 0, lengths, 2);
	CGContextStrokePath(context);

The first two examples draw a quadratic Bézier curve with one control point. The function of interest is CGContextAddQuadCurveToPoint(). The first argument is the context, followed by the x- and y- coordinates of the control points and the x- and y- coordinates of the end point. Moving the control point can dramatically change the shape of the curve.

The third example is a cubic Bézier curve with two control points. CGContextAddCurveToPoint() takes the context, the x- and y- coordinates of the first control point, the x- and y- coordinates of the second control point, and the x- and y- coordinates of the end point. The control points pull the curve along; I’ve illustrated the control path formed by the control points in a dashed gray line. Of course, all the things you can do with regular paths apply to curves as well; in fact, think of curves as a path component that you can easily append to existing paths. You can chain multiple paths together just by beginning a path, adding lines and curves, and stroking it. Now let’s talk about closing paths.

Shaping Up

A shape is simply a closed path. You can make polygons with straight lines; this code will draw a triangle:

CGContextBeginPath(context);
	CGContextMoveToPoint(context, 75, 10);
	CGContextAddLineToPoint(context, 160, 150);
	CGContextAddLineToPoint(context, 10, 150);
	CGContextClosePath(context);
	[[UIColor redColor] setFill];
	[[UIColor blackColor] setStroke];
	CGContextSetLineWidth(context, 5.0);
	CGContextSetLineJoin(context, kCGLineJoinRound);
	CGContextDrawPath(context, kCGPathFillStroke);

Everything here we’ve seen before, except for CGContextClosePath() which automatically inserts a line from the last point to the first. In addition, CGcontextSetLineJoin() is similar to setting the line cap, but used for the intersection of two paths rather than the ends. This code results in something like this:

Quartz Shapes

Quartz Shapes

Quartz includes some convenient ways to draw rectangles, ellipses, and circles.

// Draw rectangle
	CGContextBeginPath(context);
	CGContextAddRect(context, CGRectMake(200, 45, 100, 63));
	[[UIColor yellowColor] setFill];
	[[UIColor greenColor] setStroke];
	CGContextSetLineWidth(context, 3.0);
	CGContextDrawPath(context, kCGPathFillStroke);
	
	// Stroke Ellipse
	CGContextBeginPath(context);
	CGContextAddEllipseInRect(context, CGRectMake(35, 200, 180, 120));
	[[UIColor blueColor] setStroke];
	CGContextDrawPath(context, kCGPathStroke);
	
	// Fill Circle
	CGContextBeginPath(context);
	CGContextAddEllipseInRect(context, CGRectMake(220, 150, 70, 70));
	[[UIColor orangeColor] setFill];
	CGContextDrawPath(context, kCGPathFill);

Quartz provides CGContextAddRect() to draw a CGRect struct. This is an easier way to draw a rectangle than manually calculating and adding lines to points. Quartz also provides CGContextAddEllipseInRect() which draws an ellipse in the rectangle, using filling the width and height of the rectangle; the rectangle itself does not get drawn. To draw a circle, pass in a rect that has the same width and height—a square.

Drawing other polygons is a bit harder. This code snippet (from a CS193P lecture) calculates an NSArray of vertex points for a polygon of a certain number of sides in a rect:

- (NSArray *)pointsForPolygonWithSides:(NSInteger)numberOfSides inRect:(CGRect)rect {
	CGPoint center = CGPointMake(rect.size.width / 2.0, rect.size.height / 2.0);
	float radius = 0.9 * center.x;
	NSMutableArray *result = [NSMutableArray array];
	float angle = (2.0 * M_PI) / numberOfSides;
	// float exteriorAngle = M_PI - ((2.0 * M_PI) / numberOfSides);
	float rotationDelta = angle - (0.5 * (M_PI - ((2.0 * M_PI) / numberOfSides)));
	
	for (int currentAngle = 0; currentAngle < numberOfSides; currentAngle++) {
		float newAngle = (angle * currentAngle) - rotationDelta;
		float curX = cos(newAngle) * radius;
		float curY = sin(newAngle) * radius;
		[result addObject:[NSValue valueWithCGPoint:CGPointMake(center.x + curX + rect.origin.x, center.y + curY + rect.origin.y)]];
	}
	return result;
}

To draw a heptagon (7-sided polygon), add that method to CustomView.m, and use this drawing code:

// Draw heptagon
	NSArray *points = [self pointsForPolygonWithSides:7 inRect:CGRectMake(230, 250, 70, 70)];
	CGContextBeginPath(context);
	CGPoint firstPoint = [[points objectAtIndex:0] CGPointValue];
	CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
	for (int index = 1; index < [points count]; index++) {
		CGPoint nextPoint = [[points objectAtIndex:index] CGPointValue];
		CGContextAddLineToPoint(context, nextPoint.x, nextPoint.y);
	}
	CGContextClosePath(context);
	[[UIColor magentaColor] setFill];
	[[UIColor blackColor] setStroke];
	CGContextDrawPath(context, kCGPathFillStroke);

In the next section, we’ll look at the next section of Quartz graphics.

  • 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 217 other subscribers
  • Back to the Past

    May 2024
    S M T W T F S
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
  • Time Machine

  • You count!

    • 625,617 views
  • Worldwide Stats

    free counters