Advertisements

Working With Web Data


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

JSON Format

JSON begins with key-value pairs:

"fname": "Nadene"

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

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

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

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

Detail view

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

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

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

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

and use it to set the title in viewDidLoad

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

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

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

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

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

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

Contacts—People List

Contacts—People List

Contacts—Detail View

Contacts—Detail View

Generating JSON

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

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

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

Download project files

Advertisements

The Jungle, Part 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.

BlackBerry X: Initial Thoughts


Last week RIM released a preview of their new software, BlackBerry X. Normally my eyes would slide over the word “BlackBerry” like a greased weasel on skates, but something about the new release caught my attention. I had to do a triple take before I realized that, somehow, incredibly, BlackBerry X looked good. And the hardware they were using also looked good. I won’t really talk about the hardware—there’s not much to say. Ditching the physical keyboard frees them from the constraints of the plastic reality and the limited freedom of a hardware keyboard. The problem, though, is that RIM isn’t sure where it wants to go. They’ll still create a version with a physical keyboard—for the five people in the world who still like the physical keyboard. This also means that they can’t make any definitive call about having the keyboard or not. And at this point, RIM needs focus, rather than trying to appeal to everyone.

On to the software (because this is a site about software, after all). BlackBerry X features a pleasant amount of eye candy, but somehow still looks very professional. As a very satisfied iPhone user, the portions of the BlackBerry X interface seemed more polished somehow. The UI looks clean with a font suited for display. iOS uses Helvetica as its system font; while Helvetica looks timeless on paper, it is not as good on a display. The BlackBerry font is clean and modern, without being gimmicky or trendy. I noticed this in Android 4.0 (ICS), but I thought ICS had too much white-on-black. BlackBerry X keeps the clean look with more black- (and grey-) on-white. The UI elements look familiar but, again, clean and modern. Some of the icons are carried over from old BlackBerry, but other elements, including the rounded bars at the top of the screen, are decidedly iOS. Overall, however, I feel like BlackBerry X looks more professional—not button-down-and-suit-boring professional, but like the difference between metal and plastic. The iOS interface almost looks childish. I particularly like the ability to page through multiple screenfuls in a nav stack at the same time. It just feels more intuitive, and useful for those moments when you do a double-take (it’s happened to me more than you might think). Apple describes the nav controller as a deck of cards; you should be able to fan a deck of cards and peek at several. In the interest of simplicity iOS doesn’t allow this, but BlackBerry X seems to show that it works very well. It’s just not clear how exactly you’d invoke it.

The intelligent keyboard is the other major feature RIM has been touting. It uses “intelligent” algorithms to predict what you might be typing and with a flick up on the key of the software keyboard (another benefit to the software keyboard, and I suppose this feature won’t be available to the hardware keyboard model. Shame, really, that one of the major features won’t be available to a large portion of people who use BlackBerry (because they like the hardware keyboard)). The good thing is that it’s just one simple gesture—you’re flicking the word up to the text area. The negative is that it’s actually not that intuitive. When you’re typing fluently, there’s a sort of momentum in your fingers, in that it is often a lot easier to just keep typing than to use autocomplete or this new feature. Put another way, in the time it takes you to read the word suggestion, move your finger over and perform the flick gesture, you could probably have finished typing the word yourself. Only time will tell how well this feature works. I suspect a few people will use it religiously, and most will situationally appreciate it—rather like Siri on the iPhone 4S.

From what RIM’s released, I don’t think BlackBerry X will be enough to save the platform and company. But at least they’ll go out with a bang.

Design Patterns: Key-Value Observing


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

KVO Layout

KVO Structure

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

Preparing for Key-Value Observing

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

Key-Value Observing Compliance

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

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

Manually Call Change Notifications

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

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

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

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

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

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

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

Registering for Key-Value Observing

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

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

Register an Observer

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

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

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

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

Receiving Notifications

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

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

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

Removing an Observer

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

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

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

Extension: Cocoa Naming Conventions


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

General Principles

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

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

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

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

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

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

    Coding Guidelines for Cocoa.

Design Patterns: Model-View-Controller


As programs get larger, there is a greater need for a paradigm to keep everything in order. At the simplest level, any program is a means to store data and present it in a meaningful way. With only a few views and a good data store, this might be rather easy in a simple app for iPhone. But with Pages-caliber apps, especially on the iPad, a formal solution is needed. Cocoa adopts the Model-View-Controller paradigm (MVC) to better organize projects.

What is MVC?

MVC Overview

MVC Overview

Under the MVC system, your classes are one of three types—a model, a view, or a controller. Models store data in an organized form. For a simple app, the model could just be the actual data store, either in-memory (maybe as an NSArray or NSDictionary), or to-and-from disk. In a more complex app, you may choose to use a SQLite database or Core Data, and your model would be a simple instance or one piece of data. More importantly to MVC though, your model must not describe how to store the data—it must not contain any information about padding in the UI, for example, or the position (frame) of any item. Models are usually specific to your application, so they usually are not reusable, unless you have a “template” method to store data.

Views are what you actually see on-screen. They have functions to draw to screen or to other contexts. The view should not store the data it is displaying though—a label should not keep its text around; a table view should not store a copy of its data. Instead, along with MVC there are the patterns of delegation and the language features of KVC and KVO. Many of the classes from UIKit, including UILabel, UITableView, UITextView, and indeed UIView are views. Obviously, views, because they just display (any) data, can be very easily reused. Conversely, views of should not be bound to specific data. Some views may be suited to certain types of data—table views are more suited to text; the iPhone’s home screen is more suited for icons and short text—but you should not impose other restrictions, such as a maximum text length or that the text should only be of one case (that’s so 1980s…).

The view and the model should never interact. This preserves reusability. If UITableView had an outlet to your MainDataModel, it could it only be used with MainDataModel. If you had a model class called Person, it wouldn’t work with UITableView (and the Address Book app would never exist). The controller serves as a median. As shown in the image above, the controller controls interaction. If the model changes, the controller is notified and the view is changed accordingly (for example, you could call [tableView reloadData]). If user interaction changes the model (for example, deleting a row from a table view), the controller is also notified, and from there the change gets propagated to the data store. Therefore, the view can just tell the controller that some data at this location got deleted; the view does not have to worry about what to do or how to handle the deletion so it actually gets deleted. This goes back to the concept of abstraction, one of the fundamentals of object-oriented programming. Similarly, the model does not have to be concerned with exactly how the data got deleted, and just delete the data. In this way, the classes are kept clean and perform one function—the point of a class in the first place. Because the controller is the median and has to deal with specific views and data models, it is typically the least reusable. UIViewController implements so little functionality (from a functionality perspective—it actually does a lot behind the scenes) because controllers are very specific to each application. Most of your app logic goes into the controller.

Another compelling reason to use a controller is to make decisions. Obvious, but there are situations where changes to the model or view should not or need not propagate to the other. Without a controller, a change in the data at row 586 would affect the view even if the view was only showing rows 5–10, an unnecessary operation (which may even cause the UI to slow down for a moment). Other times, data should not be deleted, or deleted at a later time—if a file is being written to from another thread, a delete command from the UI should not be executed immediately. The write should be stopped or allowed to finish before the delete occurs.

Mediation

Hand-in-hand with MVC comes the concept of delegation (and data sources). Data sources are obvious—protocols such as UITableViewDataSource make it so a table view can get data from an id type, making the table view very reusable. The controller implements the delegate, and asks its data model for data as necessary (or as the data source methods are called). More interestingly though, the controller can return data that does not correspond to the data model; in fact, the controller could calculate values and not have a data model at all. The table view does not have to know about this though, and does not need to do any extra handling in these cases.

The data source protocol “pushes” data to the view; the delegate goes in the other direction and informs the controller of changes to the view. There is a naming convention involved, which will be the topic of a future post. Again, the controller can notify the model, or not, depending on the situation.

KVC and KVO are not really used in simple applications. However, with multiple controllers that need to interact with each other, KVC and KVO can be used to great benefit. KVO, which stands for Key-Value Observing, registers a class for notifications when a key-value is changed in any other class. This is a more advanced topic, and will form the basis of another post. It is an effective way to allow multiple controllers to communicate without resorting to a tangle of protocols.

Resources

WWDC 2010 had a great lecture on MVC and the “10 Best MVC Tips Ever.” It is highly recommended. Note that you will need to be a registered developer, including free developers.
Video Link | iTunes Link | Slides (9.7MB)

Advertisements
  • Welcome

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

  • Contact Me

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

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

  • Roadmap

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

    Join 223 other followers

  • Back to the Past

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

  • You count!

    • 620,617 views
  • Worldwide Stats

    free counters
%d bloggers like this: