Core Graphics Tutorial: Patterns

In this Core Graphics tutorial, you’re going to learn how to recreate a “grip” background pattern that is popular in many apps today. You know you’ve seen it out there – and you must admit, it looks pretty cool!


  • Other, Other, Other
Create a cool grip pattern effect with Core Graphics!

Create a cool grip pattern effect with Core Graphics!

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

Welcome back to another tutorial in our Core Graphics tutorial series! This tutorial series covers how to get started with Core Graphics – with practical examples.

In tutorials one, two, and three, you learned how to customize a table view from start to finish – just with Core Graphics.

In tutorial four, you learned how to make a custom glossy UIButton, just with Core Graphics.

In this Core Graphics tutorial, you’re going to learn how to recreate a “grip” background pattern that is popular in many apps today. You know you’ve seen it out there – and you must admit, it looks pretty cool!

Along the way, you’ll reinforce some of the concepts you’ve already learned about drawing arcs and shadows, and you’ll learn how to use the built-in pattern drawing capabilities of Core Graphics.

So grip your keyboard and let’s get started!

Getting Started

Start up Xcode and create a new project (File\New\Project…). Select iOS\Application\Single View Application and click Next.

Single View Application

Enter CoolPattern 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.

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

Next open up CoolPatternView.m, and add this to the top of the file:

#import "CoolPatternView.h"

static inline double radians (double degrees) 
    return degrees * M_PI/180; 
void MyDrawColoredPattern (void *info, CGContextRef context) 
    // More Coming 

This is just a helper function to convert degrees to radians, which you’ll need later. The MyDrawColoredPattern will also come into play later in the tutorial. For now, leave it as a stub.

Next uncomment drawRect, and replace the contents with the following:

- (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);

As usual, this is your test code to fill the entire view with the color red to make sure things are being drawn.

Next it’s time to set up the view. The idea is you want to be able to scroll the grip up and down a little bit and see it bounce back to the middle when you let go, just for fun.

So go ahead and open up MainStoryboard.storyboard. To make things as simple as possible, disable autolayout from being used. To do so, click the File Inspector and then deselect Use Autolayout.

Note it has the default view in there right now, but you want a UIScrollView instead. Delete the View currently in the View Controller, and drag over a Scroll View in its place. Control-drag from View Controller to the Scroll View, and set the Scroll View as the view outlet.

Next, drag a plain View inside the Scroll View as a child. You want the view to show up even as the user scrolls beyond the height of a screen, so in the Size Inspector, change the y offset to -400, and the height to 1280 to make a super-tall view that you happen to be viewing a part of.

Then go to the Identity Inspector and change the Class of the view to be a CoolPatternView.

Finally, drag a label over as a child of the Cool Pattern View, and add the text “Grip me baby one more time!” to the middle of the screen. Set the “# of Lines” for the label to be 0 (to make it multiline), increase the font size to 30, center the text, and change the font color to be white.

One final step: open ViewController.m and add these lines to the bottom of viewDidLoad to set the content size of the scroll view:

UIScrollView * scrollView = (UIScrollView *) self.view;
[scrollView setContentSize:CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height * 1.5)];

Build and run your project, and if all works well you should have a red view that you can scroll up and down for a bounce effect (still seeing red in the scrolled area):

Our grip view mocked up.

Your Goal

If you look at what a grip effect looks like zoomed in, you’ll see that it’s just a simple pattern:

Grip Effect Zoomed

The dotted line area shows the pattern that repeats. Basically there are two circles, one in the uper left and one diagonally to the right, each with a subtle shadow below the circle.

Based on the material you’ve covered already in this Core Graphics tutorial series, you already know one way to implement this: given a rectangle to fill, figure out how many times you have to repeat the pattern along the x-axis and along the y-axis, and then call the code to draw the circles/shadows in a loop.

However, when dealing with patterns that repeat like this, Core Graphics has built-in API calls to draw patterns that make it a) easier to program, and b) optimized for performance. So give that a shot!

Drawing Patterns with Core Graphics

Open up CoolPatternView.m and replace the contents of drawRect with the following:

- (void)drawRect:(CGRect)rect
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIColor * bgColor = [UIColor colorWithHue:0 saturation:0 brightness:0.15 alpha:1.0];
    CGContextSetFillColorWithColor(context, bgColor.CGColor);
    CGContextFillRect(context, rect);
    static const CGPatternCallbacks callbacks = { 0, &MyDrawColoredPattern, NULL };
    CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
    CGContextSetFillColorSpace(context, patternSpace);
    CGPatternRef pattern = CGPatternCreate(NULL,
    CGFloat alpha = 1.0;
    CGContextSetFillPattern(context, pattern, &alpha);
    CGContextFillRect(context, self.bounds);

The first thing this method does is fill the entire rectangle with a background color.

Next, the method fills in a CGPatternCallbacks structure, which is defined as this:

struct CGPatternCallbacks {
   unsigned int version;
   CGPatternDrawPatternCallback drawPattern;
   CGPatternReleaseInfoCallback releaseInfo;
typedef struct CGPatternCallbacks CGPatternCallbacks;

So basically, you pass 0 for the version, a pointer to your function that will draw the pattern (which you’ll write in a second), and NULL for the function that would be called when the pattern drawing is complete (since you don’t need one).

Next, the function creates a new color space for patterns, and sets the fill color space to that color space.

The next line is the most important part – creating the pattern object. Here it specifies the bounds of the pattern to draw (yours will be 24×24 points), the spacing to apply between tiles (in your case 24 points each), the reference to the callbacks structure you set up earlier, and a few other parameters.

Next it sets the fill pattern to the pattern object (rather than fill color), and simply fills the rectangle.

Now write the MyDrawColoredPattern callback that actually draws the pattern. Fill out the stubbed function at the top of the file. It should look like this:

void MyDrawColoredPattern (void *info, CGContextRef context) 
    UIColor * dotColor = [UIColor colorWithHue:0 saturation:0 brightness:0.07 alpha:1.0];
    UIColor * shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.1];
    CGContextSetFillColorWithColor(context, dotColor.CGColor);
    CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1, shadowColor.CGColor);
    CGContextAddArc(context, 3, 3, 4, 0, radians(360), 0);
    CGContextAddArc(context, 16, 16, 4, 0, radians(360), 0);

This function sets up two colors – an almost black color for the dot, and a white with 0.1 alpha for the shadow. It sets the first as the fill color, and the second as the shadow color (set up to be 1 point below the filled path).

It then simply draws two circles, by using the AddArc function (a circle is just an arc of 360 degrees!), and then filing the path. One circle is diagonally to the right of the other.

Build and run your code, and you should now see a cool looking grip effect!

Finished Grip Effect with Core Graphics Patterns

Core Graphics Patterns and Performance

Core Graphics patterns are amazingly fast. To see how fast, I did a little test where I reimplemented the pattern drawing in three ways:

  1. With patterns, like you did above
  2. Using a UIImage for the pattern instead
  3. Drawing the pattern in a loop with many Core Graphics calls

I then added a bit of code to record how long drawRect for each method took (the UIImage version included the time to load the image). Here were the results for my device:

Core Graphics Patterns Performance Comparison

Yowza! The nice thing is if your pattern gets more complicated (such as drawing even more shapes than the two that you drew here), the pattern drawing should still scale nicely since the draw code is only called once, then repeated.

Update: As cmar points out in the discussion below, if you use [UIColor colorWithPatternImage:...] you can get comparable results to using Core Graphics patterns directly.

So if you have effects that repeat in your projects like this, you may want to consider using Core Graphics patterns (or [UIColor colorWithPatternImage])!

Where To Go From Here?

Here is a sample project with all of the code you’ve developed in the above Core Graphics tutorial. It also includes the test code for recreating the pattern with UIImage or a drawing loop, if you want to play around with that as well.

The next Core Graphics tutorial (which is completely new!) focuses on how to draw various curves and then clone them using layers. This is all done while building a fun Twitter app. Stop on by. You’ll have a good time! :]