Core Graphics Tutorial: Lines, Rectangles, and Gradients

Ray Wenderlich

This post is also available in: Japanese, Korean

Welcome to Core Graphics 101!

Welcome to Core Graphics 101!

Update 4/15/2013 Fully updated for Xcode 4.6, and ARC. (original post by Ray Wenderlich, update by Brian Moakley).

Core Graphics is a really cool API on iOS. As a developer, you can use it to customize your UI with some really neat effects – often without even having to get an artist involved!

To many iOS developers, Core Graphics can be somewhat intimidating at first, since it’s a large API, and has plenty of snags to get caught in along the way.

This Core Graphics tutorial series is going to take the mystery out of Core Graphics and present it step by step in a series of practical exercises, starting by beautifying a table view with Core Graphics!

The table view you’ll be making will look like the above screenshot. The inspiration for this particular design cames from Bills, a beautifully designed app by PoweryBase. It’s a pretty cool app, check it out!

Note: Unfortunately, since the time of writing this article PoweryBase has shut down and no longer provides screenshots of their apps. However, you can still view their site via. the Wayback Machine.

In this this first article of the series you’ll work on making a beautiful table view cell with Core Graphics. You’ll learn how to get started with Core Graphics, how to fill and stroke rectangles, how to draw gradients, and how to deal with 1 pixel-wide line issues.

In future articles in the series, you’ll work on beautifying the rest of the app – the table view header, footer, and finishing touches.

Now crack those fingers and get comfortable. It’s time to have some fun with Core Graphics!

Getting Started

To get started, set up a project with the skeleton of the table view you want to customize. Start up Xcode and create a new project (File\New\Project…). Select Single View Application and click Next.

Project Selection

Enter CoolTable for the Product Name, enter an Organization Name (anything will do), enter a Company Identifier prefix such as com.mycompany, select iPhone for Devices, and checkmark Use Storyboards and Use Automatic Reference Counting, and finish creating the project.

Project Settings

After you finish creating the project, select both ViewController.h and ViewController.m then delete them from the project. From the delete options, select Move to Trash. You’ll need to use a UITableViewController instead.

Go to File\New File…, choose iOS\Cocoa Touch Class\Objective-C class, and click Next. In the next menu, enter the name CoolTableViewController as the class name and UITableViewController as the subclass name. Click Next then Create.

Class Creation Dialog

Select the MainStoryboard.storyboard files from the project navigator. When the storyboard is loaded, select the starting viewcontroller and delete it. Next, grab a Navigator Controller from the object library and drag it onto your storyboard. Your storyboard should now look like this:

Navigation Controller

Next, you must change the class of the UITableViewController to use your own custom class. Select the new UITableViewController and then open the Identity Inspector. In the Custom Class name field, enter CoolTableViewController.

Delete the child view controller’s navigation bar title Root View Controller. To do this, double click the child view controller’s navigation bar, select the text, and press delete.

Root View Controller

Finally, give the prototype cell a reuse identifier. To do, select the cell in the table, then click on the Attributes Inspector. In the Identifier field, type Cell.

Attributes Inspector

Now after all that work build and run. You should see the following:

Blank Table View

Now have a blank table – let’s add some sample data to it. Open up CoolTableViewController.m and make the following changes:

@interface CoolTableViewController ()
 
@property (copy) NSMutableArray *thingsToLearn;
@property (copy) NSMutableArray *thingsLearned;
 
@end

All you did here was add two arrays, which you’re going to add strings to to represent what to include in the two sections of your table. Note you added them to the private interface in the .m, because there is no need to expose these arrays to other classes.

Continue by deleting everything between @implementation CoolTableViewController and @end and replacing it with the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Core Graphics 101";	
    self.thingsToLearn = [@[@"Drawing Rects", @"Drawing Gradients", @"Drawing Arcs"] mutableCopy];
    self.thingsLearned = [@[@"Table Views", @"UIKit", @"Objective-C"] mutableCopy];
}
 
#pragma mark - Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 2;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section == 0) {
        return self.thingsToLearn.count;
    } else {
        return self.thingsLearned.count;
    }
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * CellIdentifier = @"Cell";
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    NSString * entry;
 
    if (indexPath.section == 0) {
        entry = self.thingsToLearn[indexPath.row];
    } else {
        entry = self.thingsLearned[indexPath.row];
    }
    cell.textLabel.text = entry;
 
    return cell;
}
 
-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{
    if (section == 0) {
        return @"Things We'll Learn";
    } else {
        return @"Things Already Covered";
    }
}

Ok great – you now have some sample data! Build and run the project, and you should see something like this:

Table View with Plain Style

However, if you scroll the table view up and down a bit, you’ll notice that the headers “float” as you scroll through the sections:

Table View with Plain Style - Floating Headers

This is the standard behavior for table views set with the “plain” style. However, per the emulated style that you’re going for, you don’t want the headers to float like that. They should stay with the rows as a single unit. Luckily, that is the behavior of table views set with the “grouped” style!

Open up MainStoryboard.storyboard. Inside of the CoolTableViewController scene, select the Table View, then select the Attributes Inspector. From there, set the style to Grouped:

Grouped Table

Build and run the project, and now you should have a populated (but quite vanilla) table view:

Table View with Grouped Style

Now it’s time to pretty this up with Core Graphics! Here’s how you’ll approach the update:

Table View Style Analyzed

To get the end result, you’re going to draw the table view in three different sections: the table header, cells, and footer:

Table View Analyzed

In this article, you are going to start by drawing the cells, so take a closer look at what they look like:

Table View Cells Zoomed

Note a couple things here:

  • The cells are a gradient from white to light gray.
  • Each cell has a white border around the edges to provide definition (except for the last cell, which just has it on the sides).
  • Each cell has a single gray line to separate between it and the next cell (except for the last cell).
  • The paper is indented a bit from the actual edges of the cell, to line up with the “dropped down paper” from the header.

By the way – what this simulates is light shining down on an angle to the top of an iPhone (i.e where a light would usually be in a room). So for anything that’s raised, the top should be highlighted (white) and the bottoms should have a shadow (gray). You’ll see that in a lot of UI designs, and in future articles in this series!

Note: For more information about how lighting works with graphics, check out this nice tutorial by Vicki on making objects appear 3-dimensional with light sources.

So in theory to draw the cells, you just need to know how to draw a gradient and some lines with Core Graphics. And that’s the main focus of this Core Graphics tutorial!

Hello, Core Graphics!

Whenever you want to do custom drawing on iOS, your drawing code should be within a UIView. There’s a special method called drawRect that you put all your drawing code inside.

To get started, you’ll create a “Hello, World” view that paints the entire view red, then set that as a background of your table view cell to make sure it works.

Go to File\New File…, choose iOS\Cocoa Touch Class, select Objective-C class, and click Next. In the next menu, enter the name CustomCellBackground as the class name. In the subclass field, type UIView. Click Next then Create.

You don’t need to make any changes to the header file, so switch directly over to CustomCellBackground.m and add a new drawRect method:

-(void) drawRect: (CGRect) rect 
{
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    UIColor * redColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
 
    CGContextSetFillColorWithColor(context, redColor.CGColor);
    CGContextFillRect(context, self.bounds);
}

Ok, there’s a bunch of new stuff here.

In the first line, you call a method called UIGraphicsGetCurrentContext() to get the Core Graphics Context that you’ll use in the rest of the method.

I like to think of the context as the “canvas” you’ll be painting within. In this case your “canvas” is the view, but you can get other types of contexts as well, such as an offscreen buffer that you can later turn into an image.

One of the interesting things about contexts is that they are stateful. That means that when you call methods to change something like the fill color for example, the fill color will remain set to that color until you change it to something different later.

In fact, that’s exactly what you did on the third line. You use CGContextSetFillColorWithColor to set the fill color to red. You will use this function any time you fill a shape in the future.

You might have noticed that when you called that method, you couldn’t provide a straight UIColor – you had to provide a CGColorRef instead. Luckily, it’s very easy to convert the easy-to-use UIColor to a CGColor – just by accessing the CGColor property of the UIColor.

Finally, in the last line, you call a method to fill a given rectangle (using whatever fill color has been set in the context previously). For the rectangle, you pass the bounds of the view.

Now that you have a pretty red view, you’ll set it as the background of your table view cell. To do this, add the following import to the top of CoolTableViewController.m:

#import "CustomCellBackground.h"

Then modify tableView:cellForRowAtIndexPath as follows:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * CellIdentifier = @"Cell";
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    NSString * entry;
 
    // START NEW
    if (![cell.backgroundView isKindOfClass:[CustomCellBackground class]]) {
        cell.backgroundView = [[CustomCellBackground alloc] init];
    }
 
    if (![cell.selectedBackgroundView isKindOfClass:[CustomCellBackground class]]) {
        cell.selectedBackgroundView = [[CustomCellBackground alloc] init];
    }
    // END NEW
 
    if (indexPath.section == 0) {
        entry = self.thingsToLearn[indexPath.row];
    } else {
        entry = self.thingsLearned[indexPath.row];
    }
    cell.textLabel.text = entry;
 
    cell.textLabel.backgroundColor = [UIColor clearColor]; // NEW
 
    return cell;
}

Prior to storyboards, you would have checked to see if a cell was nil, then created it. During that cell creation, you would have added the CustomCellBackgrounds to the cell. Now, you no longer have to check for nil cells since a storyboard will always return one (provided an identifier is used). Instead of checking for nil cells, you can simply check if the CustomCellBackground has already been added to the cell by inspecting the class.

The final code at the bottom of the page provides a transparent background.

Build and run the app, and you should see the following:

Hello, Core Graphics!

Awesome, you can draw with Core Graphics! And believe it or not, you already learned a bunch of really important techniques – how to get a context to draw in, how to change the fill color, and how to fill rectangles with a color. You can make some pretty nice UI with just that.

But you’re going to take it a step further, and learn about one of the most useful techniques to make excellent UIs: gradients!

Drawing Gradients

You’re going to be drawing a lot of gradients in this project, so add your gradient drawing code into a helper function. That way you won’t be repeating code throughout the various projects.

Go to File\New\File…, choose iOS\Cocoa Touch Class\Objective-C class, and click Next. In the next menu, enter the name Common as the class name. In the subclass field, type NSObject. Click Next then Create.

Now delete everything in Common.h and replace it with the following:

#import <Foundation/Foundation.h>
 
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor);

You’re not actually making a class here since you don’t need any state – you’re just defining a global function you’re about to write.

Now switch over to Common.m, delete everything inside, and replace it with the following:

#import "Common.h"
 
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };
 
    NSArray *colors = @[(__bridge id) startColor, (__bridge id) endColor];
 
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
 
    // More coming... 
}

There’s a lot in this function.

The first thing you need is to get a color space with which you’ll draw the gradient. There’s a lot you can do with color spaces, but 99% of the time you just want a standard device-dependent RGB color space, so you simply use the function CGColorSpaceCreateDeviceRGB to get the reference that you need.

Next, you set up an array that tracks the location of each color within the range of the gradient. A value of 0 would mean the start of the gradient, 1 would mean the end of the gradient. You just have two colors, and you want the first to be at the start and the second to be at the end, so you pass in 0 and 1.

Note that you could have three or more colors in a gradient if you want, and you could set where each color would begin in the gradient here. This can be useful for certain effects.

After that, you create an array with the colors that were passed into your function. You use a plain old NSArray here for convenience sake. Bridging casts are required for moving Core Foundation objects to Cocoa objects.

Note: To learn more about bridging casts and memory management in general, check out the tutorial on Automatic Reference Counting.

Then you create your gradient with CGGradientCreateWithColors, passing in the color space, color array, and locations you previously made. Note you have to convert the NSArray to a CFArrayRef – this is easy, you can just do this by casting.

You now have a gradient reference, but it hasn’t actually drawn anything yet – it’s just a pointer to the information you can use when actually drawing it later. So do that now! Add the following to drawLinearGradient after the “More coming” comment:

CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
 
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
 
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);

The first thing you do is calculate the start and end point for where you want to draw the gradient. You just set this as a line from the “top middle” to the “bottom middle” of the rectangle. Note that you use some helper functions from CGGeometry.h (such as CGRectGetMidX) to calculate these (and make your code read cleaner!)

The rest of the code helps you draw a gradient into the provided rectangle – the key function being CGContextDrawLinearGradient. The weird thing about that function, though, is that it fills up the entire drawing region with the gradient. I.e. there’s no way to set it to only fill up a sub area with the gradient.

Well… without clipping, that is! Clipping is an awesome feature in Core Graphics where you can restrict drawing to an arbitrary shape. All you have to do is add the shape to the context, but then instead of filling it like you usually would, you call CGContextClip. And all future drawing will be restricted to that region!

So that’s what you do here. You add your rectangle to the context, clip to that, then call CGContextDrawLinearGradient, passing in all of the variables that you’ve set up before.

So what’s this stuff about CGContextSaveCGState/CGContextRestoreCGState all about? Well, remember that Core Graphics is a state machine, and once you’ve set something it stays that way until you change it back.

Well, you’ve just clipped to a region, so unless you do something about it you’ll never be able to draw outside of that region again!

Well that’s where CGContextSaveCGState/CGContextRestoreCGState come to the rescue. With these you can save the current setup of your context to a stack, and then pop it back later when you’re done and get back to where you were.

There’s just one last thing – you need to call CGGradientRelease to free up the memory created by CGGradientCreateWithColors earlier (and CGColorSpaceRelease too, thanks @Jim!).

That’s it! So give this function a shot inside your cell background. Open up CustomCellBackground.m import your new header at the top of the file:

#import "Common.h"

Then replace drawRect: with the following:

-(void) drawRect: (CGRect) rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    UIColor * whiteColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; 
    UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
 
    CGRect paperRect = self.bounds;
 
    drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
}

Build and run< your project, and you should see the following:

Cells with Gradients

Pretty cool, eh? It’s amazing what a simple gradient can do!

Stroking Paths

It’s looking good so far, but you’re going to add some subtle touches to make it “pop” just a little bit more. You’ll draw a white rectangle around the edges, and a gray separator between cells.

You already know how to fill rectangles – well it turns out drawing lines around the rectangles (i.e. stroking rectangles) is just as easy!

Modify drawRect: in CustomCellBackground.m as follows:

-(void) drawRect: (CGRect) rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    UIColor * whiteColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; 
    UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
    UIColor * redColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]; // NEW
 
    CGRect paperRect = self.bounds;
 
    drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
 
    // START NEW
    CGRect strokeRect = CGRectInset(paperRect, 5.0, 5.0);
    CGContextSetStrokeColorWithColor(context, redColor.CGColor);
    CGContextSetLineWidth(context, 1.0);
    CGContextStrokeRect(context, strokeRect);
    // END NEW
 
}

You’re going to draw this rectangle with a red stroke and make it in the middle of the cell, in order to make it easy to see at first. So you create a color, and shrink the rectangle a bit with the CGRectInset helper function.

If you haven’t seen it before, all the CGRectInset method does is subtract a given amount from the X and Y sides of a rectangle, and return to you the results.

You then set the stroke color to red, set the line width to 1 point wide, and call CGContextStrokeRect to stroke the rectangle.

Build and run the project and you’ll see the following:

Fuzzy 1 Pixel Lines in Core Graphics

It looks OK at first… but then again, maybe a little fuzzy or weird eh? Well if you zoom in you’ll see some odd behavior:

Fuzzy 1 Pixel Lines in Core Graphics - Zoomed

You told it to draw with 1 point (which should equate to 1 pixel for non-Retina displays, or 2 pixels for Retina displays), but it appears to actually be drawing over multiple pixels… what’s going on?

1 Point Lines and Pixel Boundaries

Well, it turns out that when Core Graphics strokes a path, it draws the stroke on the middle of the exact edge of the path.

In your case, the edge of the path is the rectangle you wish to fill. So when drawing a 1 pixel line along that edge, half of the line (1/2 pixel) will be on the inside of the rectangle, and the other half of the line (1/2 pixel) will be on the outside of the rectangle.

But of course, since there’s no way to draw 1/2 a pixel, instead Core Graphics uses anti-aliasing to draw in both pixels, but just a lighter shade to give the appearance that it is only a single pixel drawn.

But you don’t want no anti-aliasing, you want just one pixel, darnit! There are several ways to fix this:

  • You can use clipping to cut out the undesirable pixels
  • You can disable antialiasing and also modify the rectangle boundaries to make sure the stroke is where you want
  • You can modify the path to stroke so it takes the 1/2 pixel effect into consideration

In this Core Graphics tutorial, you’re going to go with option #3 and modify the rectangle to take the stroke behavior into consideration. To make this easier, create a helper method to modify a rectangle for a 1 pixel stroke.

To do so, open up Common.h and add the following declaration at the bottom of the file:

CGRect rectFor1PxStroke(CGRect rect);

Then add the following modification to Common.m:

CGRect rectFor1PxStroke(CGRect rect) 
{
    return CGRectMake(rect.origin.x + 0.5, rect.origin.y + 0.5, rect.size.width - 1, rect.size.height - 1);
}

Here you modify the rectangle so the edge is halfway through the inside pixel of the original rectangle, so the stroke behavior works correctly.

So back in CustomCellBackground.m, replace the line that initializes strokeRect with the following:

CGRect strokeRect = rectFor1PxStroke(CGRectInset(paperRect, 5.0, 5.0));

Now if you build and run the rectangle strokes should be nice and sharp:

1 Pixel Lines in Core Graphics - Sharp

Ok nice. Now finish this up to make it the right color and location. Modify drawRect in CustomCellBackground.m as follows:

-(void) drawRect: (CGRect) rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    UIColor * whiteColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; 
    UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
    // UIColor * redColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
 
    CGRect paperRect = self.bounds;
 
    drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
 
    // START NEW
    CGRect strokeRect = paperRect;
    strokeRect.size.height -= 1;
    strokeRect = rectFor1PxStroke(strokeRect);
    CGContextSetStrokeColorWithColor(context, whiteColor.CGColor);
    // END NEW
 
    CGContextSetLineWidth(context, 1.0);
    CGContextStrokeRect(context, strokeRect);
 
}

Here you reduce the height of the paper rect by 1 to leave room for the separator, convert it, and stroke the color with white.

Build and run your project, and there should now be a subtle white border around the cells:

Custom Cells with White Border

Next, add a light gray separator between cells!

Drawing Lines

Since you’re going to be drawing several lines in your project, you should make a helper method for it. Add the following to Common.h:

void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint, CGColorRef color);

And the following to Common.m:

void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint, CGColorRef color) 
{
    CGContextSaveGState(context);
    CGContextSetLineCap(context, kCGLineCapSquare);
    CGContextSetStrokeColorWithColor(context, color);
    CGContextSetLineWidth(context, 1.0);
    CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
    CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);           
}

At the beginning and end of the function, you save/restore the context so you don’t leave any of the changes you made around.

Then you set the line cap of the line. The default is for a line to have a “butt” ending. No, this has nothing to do with a human posterior, instead it means the line ends EXACTLY at the ending point of the line.

I like CG butts!

This isn’t good for you since you’re starting and ending the line 1/2 point in to fix the stroke behavior. So instead, you set the line cap to have a “square” ending, which makes the line extend 1/2 of the line width beyond the end – in your case 1/2 point – perfect!

You then set the color and line width as usual.

You then do the actual drawing of a line. To draw a line in Core Graphics, you first move to point A (nothing is drawn yet), and then add a line to point B (which adds the line from point A to point B into the context). You can then call CGContextStrokePath to stroke the line.

That’s it! Now, implement it to draw a separator line by making the following changes to drawRect in CustomCellBackground.m:

-(void) drawRect: (CGRect) rect
{
    ...
    UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0]; // Previous Code
    UIColor * separatorColor = [UIColor colorWithRed:208.0/255.0 green:208.0/255.0 blue:208.0/255.0 alpha:1.0];
 
    ...
 
    CGPoint startPoint = CGPointMake(paperRect.origin.x, paperRect.origin.y + paperRect.size.height - 1);
    CGPoint endPoint = CGPointMake(paperRect.origin.x + paperRect.size.width - 1, paperRect.origin.y + paperRect.size.height - 1);
    draw1PxStroke(context, startPoint, endPoint, separatorColor.CGColor); 
}

Build and run your project, and now there should be a nice separator between the cells!

Custom Cells with Separator

Where To Go From Here?

Here is a sample project with all of the code you’ve developed so far in the above Core Graphics tutorial.

At this point you should be familiar with some pretty cool and powerful techniques with Core Graphics – filling and stroking rectangles, drawing lines and gradients, and clipping to paths! Not to mention your table view is starting to look pretty cool.

But there’s more! You haven’t learned how to add drop shadows yet, or arcs, gloss effects, and other cool stuff – which you will cover in the next Core Graphics tutorial as you add a cool looking header to your table view!

In the meantime, if you have any questions, suggestions, or comments, please fire away! :]

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

12 Comments

  • Thanks for this update.
    swe19
  • Great update, this would have to be the tutorial I most wanted to see updated.
    northy179
  • The link at the end of the article is broken (the link to part II of the tutorial)
    quinrado
  • Just a quick question, why not just create a custom class for the tableView cell and override drawRect: within the custom cell class? It seems like that might make more sense than using a custom UIView, and heck, when you create a subclass of UITableViewCell, it places a commented out drawRect: method in the .m file for you.

    Just trying to clear up why you chose to use a UIView and set the background view of the cell, rather than draw on the cell itself.
    TLewis
  • Also, an easy way to set this up would be to use UIAppearance. You would make a subclass of UITableViewCell like normal, and then in the .h file do this:

    Code: Select all
    - (void)setBackgroundView:(UIView *)backgroundView UI_APPEARANCE_SELECTOR;


    and then in the .m do this:

    Code: Select all
    - (void)setBackgroundView:(UIView *)backgroundView {
        [super setBackgroundView:backgroundView];
    }


    And finally, in your app delegate just do this:

    Code: Select all
    [[CustomCellClass appearance]setBackgroundView:[[CustomCellBackground alloc]init]];


    and then bam! You have your custom background set just like that. You can also do the same thing for the selectedBackgroundView, just make sure you put the UI_APPEARANCE_SELECTOR after the method definition.
    TLewis
  • I am applying the gradient to the UITableView cell. My cell has dynamic height. If my cell's height is large , it shows gradient 2 times for single cell. Here is my code & screenshot http://stackoverflow.com/questions/16279589/add-gradient-to-table-cell-with-dynamic-height-ios-6 of how it looks like. Can you tell me whats going wrong here ?
    Thanks in advance.
    appDev
  • In the prototype cells' Attributes Inspector the background is set to "No Color". However, in my case the default was set to some white-ish color and I couldn't change it to "No Color". I have no idea why. I worked around the problem by selecting "other" and changing the opacity to 0.
    Shai
  • I am setting up my own custom drawing method, for my cell's UIView... I just want a simple off white cell with a dark line as the spacer between cells... The only problem is this dark line is drawn on the bottom most cell which makes it look strange. From examining your screenshots you seem to have avoided your spacer line being drawn on the bottom cell, how have you managed to do that?
    simonthumper
  • TLewis wrote:Also, an easy way to set this up would be to use UIAppearance. You would make a subclass of UITableViewCell like normal, and then in the .h file do this:

    Code: Select all
    - (void)setBackgroundView:(UIView *)backgroundView UI_APPEARANCE_SELECTOR;


    and then in the .m do this:

    Code: Select all
    - (void)setBackgroundView:(UIView *)backgroundView {
        [super setBackgroundView:backgroundView];
    }


    And finally, in your app delegate just do this:

    Code: Select all
    [[CustomCellClass appearance]setBackgroundView:[[CustomCellBackground alloc]init]];


    and then bam! You have your custom background set just like that. You can also do the same thing for the selectedBackgroundView, just make sure you put the UI_APPEARANCE_SELECTOR after the method definition.


    This doesn't seem to work when using a static table for some reason, I had thought it might if I set the class correctly in interface builder, but it just refuses to work :/
    simonthumper
  • Hello,
    I begin learning IOS and i want to create a image with areas event gesture. Is it possible to add gesture event in drawing area ? (sorry for my english... ;-)
    manheman
  • The final stroke drawn at the end of this tutorial doesn't work on iOS 7 (iPad).
    DanMoore24
  • As always we all depend on this site for great tutorials. My question is why are you using drawRect instead of layoutSubviews?
    Dont a get a performance hit here using drawRect?
    Thank you in advance,
    mjeragh

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in July: Facebook Pop Tech Talk!

Sign Up - July

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Jean-Pierre Distler

... 49 total!

Update Team

  • Ray Fix

Editorial Team

... 23 total!

Code Team

  • Orta Therox

... 1 total!

Translation Team

  • Team Tyran
  • Jose De La Roca
  • Heejun Han

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!