Everything you need is covered in the Quartz 2D Programming Guide. I suggest you look through it.
However, it can be difficult to put it all together, so I'll walk you through it. We'll write a function that takes a size and returns an image that looks roughly like one of your segments:

We start the function definition like this:
static UIImage *imageWithSize(CGSize size) {
We'll need a constant for the thickness of the segment:
static CGFloat const kThickness = 20;
and a constant for the width of the line outlining the segment:
static CGFloat const kLineWidth = 1;
and a constant for the size of the shadow:
static CGFloat const kShadowWidth = 8;
Next we need to create an image context in which to draw:
UIGraphicsBeginImageContextWithOptions(size, NO, 0); {
I put a left brace on the end of that line because I like an extra level of indentation to remind me to call UIGraphicsEndImageContext
later.
Since a lot of the functions we need to call are Core Graphics (aka Quartz 2D) functions, not UIKit functions, we need to get the CGContext
:
CGContextRef gc = UIGraphicsGetCurrentContext();
Now we're ready to really get started. First we add an arc to the path. The arc runs along the center of the segment we want to draw:
CGContextAddArc(gc, size.width / 2, size.height / 2,
(size.width - kThickness - kLineWidth) / 2,
-M_PI / 4, -3 * M_PI / 4, YES);
Now we'll ask Core Graphics to replace the path with a “stroked” version that outlines the path. We first set the thickness of the stroke to the thickness we want the segment to have:
CGContextSetLineWidth(gc, kThickness);
and we set the line cap style to “butt” so we'll have squared-off ends:
CGContextSetLineCap(gc, kCGLineCapButt);
Then we can ask Core Graphics to replace the path with a stroked version:
CGContextReplacePathWithStrokedPath(gc);
To fill this path with a linear gradient, we have to tell Core Graphics to clip all operations to the interior of the path. Doing so will make Core Graphics reset the path, but we'll need the path later to draw the black line around the edge. So we'll copy the path here:
CGPathRef path = CGContextCopyPath(gc);
Since we want the segment to cast a shadow, we'll set the shadow parameters before we do any drawing:
CGContextSetShadowWithColor(gc,
CGSizeMake(0, kShadowWidth / 2), kShadowWidth / 2,
[UIColor colorWithWhite:0 alpha:0.3].CGColor);
We're going to both fill the segment (with a gradient) and stroke it (to draw the black outline). We want a single shadow for both operations. We tell Core Graphics that by beginning a transparency layer:
CGContextBeginTransparencyLayer(gc, 0); {
I put a left brace on the end of that line because I like to have an extra level of indentation to remind me to call CGContextEndTransparencyLayer
later.
Since we're going to change the context's clip region for filling, but we won't want to clip when we stroke the outline later, we need to save the graphics state:
CGContextSaveGState(gc); {
I put a left brace on the end of that line because I like to have an extra level of indentation to remind me to call CGContextRestoreGState
later.
To fill the path with a gradient, we need to create a gradient object:
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(rgb, (__bridge CFArrayRef)@[
(__bridge id)[UIColor grayColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor
], (CGFloat[]){ 0.0f, 1.0f });
CGColorSpaceRelease(rgb);
We also need to figure out a start point and an end point for the gradient. We'll use the path bounding box:
CGRect bbox = CGContextGetPathBoundingBox(gc);
CGPoint start = bbox.origin;
CGPoint end = CGPointMake(CGRectGetMaxX(bbox), CGRectGetMaxY(bbox));
and we'll force the gradient to be drawn either horizontally or vertically, whichever is longer:
if (bbox.size.width > bbox.size.height) {
end.y = start.y;
} else {
end.x = start.x;
}
Now we finally have everything we need to draw the gradient. First we clip to the path:
CGContextClip(gc);
Then we draw the gradient:
CGContextDrawLinearGradient(gc, gradient, start, end, 0);
Then we can release the gradient and restore the saved graphics state:
CGGradientRelease(gradient);
} CGContextRestoreGState(gc);
When we called CGContextClip
, Core Graphics reset the context's path. The path isn't part of the saved graphics state; that's why we made a copy earlier. Now it's time to use that copy to set the path in the context again:
CGContextAddPath(gc, path);
CGPathRelease(path);
Now we can stroke the path, to draw the black outline of the segment:
CGContextSetLineWidth(gc, kLineWidth);
CGContextSetLineJoin(gc, kCGLineJoinMiter);
[[UIColor blackColor] setStroke];
CGContextStrokePath(gc);
Next we tell Core Graphics to end the transparency layer. This will make it look at what we've drawn and add the shadow underneath:
} CGContextEndTransparencyLayer(gc);
Now we're all done drawing. We ask UIKit to create a UIImage
from the image context, then destroy the context and return the image:
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
You can find the code all together in this gist.