I think that @ChrisMiles is correct in that the segments are probably being drawn individually. (I initially thought that this might have been doable using CGPatternRef
but you don't have any access to the CTM or path endpoints inside the pattern drawing callback.)
With this in mind, here is an exceedingly crude, back-of-the-envelope example of how you might begin such an effort (filling the segments individually). Note that:
- gradient colors are guessed
- end caps are nonexistent and will need to be separately implemented
- some aliasing artifacts remain
- not a great deal of attention was paid to performance
Hopefully this can get you started at least (and works through some of the analytic geometry).
- (CGGradientRef)lineGradient
{
static CGGradientRef gradient = NULL;
if (gradient == NULL) {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef white = [[UIColor colorWithWhite:1.f
alpha:0.7f] CGColor];
CGColorRef blue = [[UIColor colorWithRed:0.1f
green:0.2f
blue:1.f
alpha:0.7f] CGColor];
CGColorRef lightBlue = [[UIColor colorWithRed:0.4f
green:0.6f
blue:1.f
alpha:0.7f] CGColor];
CFMutableArrayRef colors = CFArrayCreateMutable(kCFAllocatorDefault,
8,
NULL);
CFArrayAppendValue(colors, blue);
CFArrayAppendValue(colors, blue);
CFArrayAppendValue(colors, white);
CFArrayAppendValue(colors, white);
CFArrayAppendValue(colors, lightBlue);
CFArrayAppendValue(colors, lightBlue);
CFArrayAppendValue(colors, blue);
CFArrayAppendValue(colors, blue);
CGFloat locations[8] = {0.f, 0.08f, 0.14f, 0.21f, 0.29f, 0.86f, 0.93f, 1.f};
gradient = CGGradientCreateWithColors(colorSpace,
colors,
locations);
CFRelease(colors);
CGColorSpaceRelease(colorSpace);
}
return gradient;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetShouldAntialias(context, YES);
// Fill background color
[[UIColor whiteColor] setFill];
UIRectFill(rect);
// Build a path
CGFloat strokeWidth = 10.f;
CGContextSetLineWidth(context, strokeWidth);
CGGradientRef gradient = [self lineGradient];
CGPoint points[9] = {
CGPointMake(10.f, 25.f),
CGPointMake(100.f, 100.f),
CGPointMake(100.f, 150.f),
CGPointMake(22.f, 300.f),
CGPointMake(230.f, 400.f),
CGPointMake(230.f, 200.f),
CGPointMake(300.f, 200.f),
CGPointMake(310.f, 160.f),
CGPointMake(280.f, 100.f)
};
for (NSUInteger i = 1; i < 9; i++) {
CGPoint start = points[i - 1];
CGPoint end = points[i];
CGFloat dy = end.y - start.y;
CGFloat dx = end.x - start.x;
CGFloat xOffset, yOffset;
// Remember that, unlike Cartesian geometry, origin is in *upper* left!
if (dx == 0) {
// Vertical to start, gradient is horizontal
xOffset = 0.5 * strokeWidth;
yOffset = 0.f;
if (dy < 0) {
xOffset *= -1;
}
}
else if (dy == 0) {
// Horizontal to start, gradient is vertical
xOffset = 0.f;
yOffset = 0.5 * strokeWidth;
}
else {
// Sloped
CGFloat gradientSlope = - dx / dy;
xOffset = 0.5 * strokeWidth / sqrt(1 + gradientSlope * gradientSlope);
yOffset = 0.5 * strokeWidth / sqrt(1 + 1 / (gradientSlope * gradientSlope));
if (dx < 0 && dy > 0) {
yOffset *= -1;
}
else if (dx > 0 && dy < 0) {
xOffset *= -1;
}
else if (dx < 0 && dy < 0) {
yOffset *= -1;
xOffset *= -1;
}
else {
}
}
CGAffineTransform startTransform = CGAffineTransformMakeTranslation(-xOffset, yOffset);
CGAffineTransform endTransform = CGAffineTransformMakeTranslation(xOffset, -yOffset);
CGPoint gradientStart = CGPointApplyAffineTransform(start, startTransform);
CGPoint gradientEnd = CGPointApplyAffineTransform(start, endTransform);
CGContextSaveGState(context);
CGContextMoveToPoint(context, start.x, start.y);
CGContextAddLineToPoint(context, end.x, end.y);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
CGContextDrawLinearGradient(context,
gradient,
gradientStart,
gradientEnd,
kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation);
CGContextRestoreGState(context);
}
CGContextRestoreGState(context);
}