绘制类似谷歌地图方向指示的MKMapView覆盖层

10

iOS 5改变了内置的Google Maps应用程序绘制路线的方式:

Apple's line

我现在想在自己的应用程序中复制路线覆盖层的设计,但目前我只能绘制一个普通的蓝色线。我想添加具有渐变、边框和发光效果的3D效果。有什么建议吗?

目前,我正在使用以下代码:

CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextAddPath(context, path);
CGContextReplacePathWithStrokedPath(context);
CGContextFillPath(context);

导致一条相当丑陋的线:

my line

谢谢!

更新:解决方案应适用于iOS 4.0及以上版本。


你能否发布一下你目前用于绘制线条的代码? - sudo rm -rf
@sudo rm -rf:我已经添加了绘图代码。 - myell0w
2个回答

7
我认为@ChrisMiles是正确的,分段可能是单独绘制的。(我最初认为可以使用CGPatternRef进行操作,但您在模式绘制回调中没有访问CTM或路径端点。)
考虑到这一点,以下是一个极为简陋的草稿示例,说明如何开始这样的努力(单独填充各段)。请注意:
渐变颜色是猜测的
末端帽不存在,需要单独实现
一些锯齿效应仍然存在
没有太多关注性能
希望这至少可以让您开始工作(并解决一些解析几何问题)。
-  (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);
}

1

我认为他们正在围绕原始线条绘制CGPath,描边并渐变填充它。通过向CGPath添加半圆来封闭末端。

这比仅绘制一条线并描边要更费力,但可以完全控制呈现路径的样式。


听起来没错,克里斯,谢谢。我的问题是如何沿着CGPath绘制渐变。你有什么想法或者代码片段可以分享吗? - myell0w
我不知道结果会是什么样子,但你可以使用 CGContextReplacePathWithStrokedPath() 来检索轮廓,并填充渐变。这个之前的问题可能会有帮助:https://dev59.com/wHE85IYBdhLWcg3whD3j - lxt
经过一些尝试,我发现没有办法轻松地让渐变填充沿着路径进行,就像他们所做的那样。他们可能会分解路径并为每个段单独绘制渐变填充。 - Chris Miles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接