ios - 带有 MapKit ios 的渐变折线

标签 ios mkmapview overlay mapkit gradient

我正在尝试使用叠加层 (MKOverlay) 在 MKMapView 上追踪路线。 但是,根据当前速度,如果颜色在变化(例如,如果用户从 65mph 行驶到 30mph,则从绿色变为橙色),我想在追踪路线时做一些类似 Nike 应用程序的渐变。

这是我想要的截图:

gradient overlay image

因此,每 20 米,我使用以下方法添加一个从旧坐标到新坐标的叠加层:

// Create a c array of points. 
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D) * 2);

// Create 2 points.
MKMapPoint startPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(oldLatitude, oldLongitude));
MKMapPoint endPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(newLatitude, newLongitude));

// Fill the array.
pointsArray[0] = startPoint;
pointsArray[1] = endPoint;

// Erase polyline and polyline view if not nil.
if (self.routeLine != nil)
    self.routeLine = nil;

if (self.routeLineView != nil)
    self.routeLineView = nil;

// Create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];

// Add overlay to map.
[self.mapView addOverlay:self.routeLine];

// clear the memory allocated earlier for the points.
free(pointsArray);

// Save old coordinates.
oldLatitude = newLatitude;
oldLongitude = newLongitude;

基本上我添加了很多小叠加层。然后我想在这个小线条图上创建渐变,所以我试图在覆盖委托(delegate)中这样做:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
    MKOverlayView* overlayView = nil;

    if(overlay == self.routeLine) {
        // If we have not yet created an overlay view for this overlay, create it now. 
        if(self.routeLineView == nil) {
            self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];

            if (speedMPH < 25.0) {
                self.routeLineView.fillColor = [UIColor redColor];
                self.routeLineView.strokeColor = [UIColor redColor];
            }
            else if (speedMPH >= 25.0 && speedMPH < 50.0) {
                self.routeLineView.fillColor = [UIColor orangeColor];
                self.routeLineView.strokeColor = [UIColor orangeColor];
            }
            else {
                self.routeLineView.fillColor = [UIColor greenColor];
                self.routeLineView.strokeColor = [UIColor greenColor];
            }

            // Size of the trace.
            self.routeLineView.lineWidth = routeLineWidth;

            // Add gradient if color changed.
            if (oldColor != self.routeLineView.fillColor) {
                CAGradientLayer *gradient = [CAGradientLayer layer];
                    gradient.frame = self.routeLineView.bounds;
                    gradient.colors = [NSArray arrayWithObjects:(id)[oldColor CGColor], (id)[self.routeLineView.fillColor CGColor], nil];
                    [self.routeLineView.layer insertSublayer:gradient atIndex:0];
            }

            // Record old color for gradient.
            if (speedMPH < 25.0)
                oldColor = [UIColor redColor];
            else if (speedMPH >= 25.0 && speedMPH < 50.0)
                oldColor = [UIColor orangeColor];
            else
                oldColor = [UIColor greenColor];
        }

        overlayView = self.routeLineView;
    }

    return overlayView;
}

我正在尝试以这种方式添加渐变,但我想这不是这样做的方式,因为我无法让它工作。

我还可以在每次用户位置更新时(在位置对象的委托(delegate)中)或如上所述每 20 米跟踪一次路线。

你能帮我解决这个问题吗,给我一些提示! 谢谢!

最佳答案

我想到的一个想法是创建一个 CGPath 并在每次调用 drawMapRect 方法时用渐变描边,因为 MKPolylineView 被替换为 MKPlolylineRenderer in ios7.

我试图通过子类化 MKOverlayPathRenderer 来实现这一点,但我未能挑选出单独的 CGPath,然后我找到了一个名为 -(void) strokePath:(CGPathRef)path inContext 的神秘方法: (CGContextRef)context 这听起来像我需要的,但如果您在覆盖 drawMapRect 时不调用 super 方法,则不会调用它。

这就是我现在的工作。

我会继续努力,所以如果我解决了问题,我会回来更新答案。

=========更新============================== ==================

enter image description here

所以这就是我这几天的工作,我几乎实现了上面提到的基本想法但是是的,我仍然无法根据特定的mapRect选择出一个单独的PATH,所以我只是把所有的路径都用gradient同时绘制所有路径的boundingBox与当前mapRect相交的时间。糟糕的把戏,但现在就开始吧。

在渲染类的 -(void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context 方法中,我这样做:

CGMutablePathRef fullPath = CGPathCreateMutable();
BOOL pathIsEmpty = YES;

//merging all the points as entire path
for (int i=0;i< polyline.pointCount;i++){
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    if (pathIsEmpty){
        CGPathMoveToPoint(fullPath, nil, point.x, point.y);
        pathIsEmpty = NO;
    } else {
        CGPathAddLineToPoint(fullPath, nil, point.x, point.y);
    }
}

//get bounding box out of entire path.
CGRect pointsRect = CGPathGetBoundingBox(fullPath);
CGRect mapRectCG = [self rectForMapRect:mapRect];
//stop any drawing logic, cuz there is no path in current rect.
if (!CGRectIntersectsRect(pointsRect, mapRectCG))return;

然后我将整个路径逐点拆分以单独绘制其渐变。 请注意,hues 数组包含映射每个位置速度的色调值。

for (int i=0;i< polyline.pointCount;i++){
    CGMutablePathRef path = CGPathCreateMutable();
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    ccolor = [UIColor colorWithHue:hues[i] saturation:1.0f brightness:1.0f alpha:1.0f];
    if (i==0){
        CGPathMoveToPoint(path, nil, point.x, point.y);
    } else {
        CGPoint prevPoint = [self pointForMapPoint:polyline.points[i-1]];
        CGPathMoveToPoint(path, nil, prevPoint.x, prevPoint.y);
        CGPathAddLineToPoint(path, nil, point.x, point.y);
        CGFloat pc_r,pc_g,pc_b,pc_a,
        cc_r,cc_g,cc_b,cc_a;
        [pcolor getRed:&pc_r green:&pc_g blue:&pc_b alpha:&pc_a];
        [ccolor getRed:&cc_r green:&cc_g blue:&cc_b alpha:&cc_a];
        CGFloat gradientColors[8] = {pc_r,pc_g,pc_b,pc_a,
                                    cc_r,cc_g,cc_b,cc_a};

        CGFloat gradientLocation[2] = {0,1};
        CGContextSaveGState(context);
        CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){self.lineWidth,self.lineWidth}).width;
        CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, self.lineCap, self.lineJoin, self.miterLimit);
        CGContextAddPath(context, pathToFill);
        CGContextClip(context);//<--clip your context after you SAVE it, important!
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocation, 2);
        CGColorSpaceRelease(colorSpace);
        CGPoint gradientStart = prevPoint;
        CGPoint gradientEnd = point;
        CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation);
        CGGradientRelease(gradient);
        CGContextRestoreGState(context);//<--Don't forget to restore your context.
    }
    pcolor = [UIColor colorWithCGColor:ccolor.CGColor];
}

这就是所有核心绘图方法,当然您需要在覆盖类中使用 pointsvelocity 并使用 CLLocationManager 提供它们。

最后一点是如何从速度中获取 hue 值,好吧,我发现如果 hue 范围从 0.03~0.3 正好代表从红色到绿色,所以我做了一些比例映射到色调和速度。

最后的,给你这是这个演示的完整源代码:https://github.com/wdanxna/GradientPolyline

如果看不到你画的线不要 panic ,我只是把 map 区域定位在我的位置:)

关于ios - 带有 MapKit ios 的渐变折线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5682688/

相关文章:

ios - 从 mapView.annotations 的自定义 MKAnnotation 子类中检索属性

android - TileOverlay 与 android 和谷歌地图

html - 带有滚动和自动大小列的表格

ios - 使用 OpenCv 在 IOS 中裁剪圆形图像

ios - 在 ios 中创建自定义标注

ios - 重新加载后 uicollectionviewcell 标签未对齐

ios - MKMapview 中 poi 点的详细信息

html - 如何使用 z-index 和不透明度在父元素前面显示子元素?

iphone - 重定向 UIAlertView 按钮推送通知

ios - 如何启用滑动手势以在 Xamarin 表单 UWP 中引入母版页?