使用MapKit ios的渐变折线

Dac*_*hmt 20 overlay gradient mapkit mkmapview ios

我正在尝试使用叠加(MKOverlay)跟踪MKMapView上的路线.但是,根据当前的速度,如果颜色正在变化(例如,如果用户从65mph驱动到30mph),我想要在跟踪路线时使用渐变来执行类似Nike应用程序的操作(例如,从绿色变为橙色).

这是我想要的截图:

渐变叠加图像

因此,每隔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;
Run Code Online (Sandbox Code Playgroud)

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

- (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;
}
Run Code Online (Sandbox Code Playgroud)

我试图以这种方式添加渐变,但我想这不是这样做的方法,因为我无法使其工作.

每次有关用户位置(位置对象的代表)的更新时,或者每隔20米更新一次,我也可以跟踪路线.

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

wda*_*xna 15

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

我尝试通过子类化来实现这个MKOverlayPathRenderer但是我没有选择单独的CGPath,然后我发现一个神秘的方法命名-(void) strokePath:(CGPathRef)path inContext:(CGContextRef)context,听起来像我需要的,但如果你在覆盖你的时候不调用super方法就不会调用它drawMapRect.

这就是我现在正在努力的事情.

我会继续努力,如果我找到了什么我会回来并更新答案.

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

在此输入图像描述

所以这就是我现在所做的,我几乎实现了上面提到的基本思想但是,我仍然无法根据具体的mapRect选择单独的PATH,所以我只是在同时绘制所有具有渐变的路径所有路径的boundingBox与当前的mapRect相交.可怜的伎俩,但现在工作.

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

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;
Run Code Online (Sandbox Code Playgroud)

然后我逐点分割整个路径以单独绘制其渐变.注意,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];
}
Run Code Online (Sandbox Code Playgroud)

这是所有的核心绘图方法,当然你需要points,velocity你覆盖类以及CLLocationManager养活他们.

最后一点是如何hue从速度中获取值,我发现如果0.03~0.3范围内的色调恰好代表从红色到绿色,那么我会按比例映射到色调和速度.

最后一个,在这里你是这个演示的完整来源:https://github.com/wdanxna/GradientPolyline

不要惊慌,如果看不到你画的线,我只是将地图区域定位在我的位置上:)


gug*_*gge 10

我实现了一个受上述@wdanxna 解决方案启发的Swift 4版本。有些事情发生了变化,路径已经在超类中创建。

我没有将色调存储在渲染器中,而是创建了一个 MKPolyline 的子类,用于在构造函数中计算色调。然后我从渲染器中获取带有值的折线。我将它映射到速度,但我想你可以将渐变映射到任何你想要的。

渐变折线

class GradientPolyline: MKPolyline {
    var hues: [CGFloat]?
    public func getHue(from index: Int) -> CGColor {
        return UIColor(hue: (hues?[index])!, saturation: 1, brightness: 1, alpha: 1).cgColor
    }
}

extension GradientPolyline {
    convenience init(locations: [CLLocation]) {
        let coordinates = locations.map( { $0.coordinate } )
        self.init(coordinates: coordinates, count: coordinates.count)

        let V_MAX: Double = 5.0, V_MIN = 2.0, H_MAX = 0.3, H_MIN = 0.03

        hues = locations.map({
            let velocity: Double = $0.speed

            if velocity > V_MAX {
                return CGFloat(H_MAX)
            }

            if V_MIN <= velocity || velocity <= V_MAX {
                return CGFloat((H_MAX + ((velocity - V_MIN) * (H_MAX - H_MIN)) / (V_MAX - V_MIN)))
            }

            if velocity < V_MIN {
                return CGFloat(H_MIN)
            }

            return CGFloat(velocity)
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

渐变折线渲染器

class GradidentPolylineRenderer: MKPolylineRenderer {

    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        let boundingBox = self.path.boundingBox
        let mapRectCG = rect(for: mapRect)

        if(!mapRectCG.intersects(boundingBox)) { return }


        var prevColor: CGColor?
        var currentColor: CGColor?

        guard let polyLine = self.polyline as? GradientPolyline else { return }

        for index in 0...self.polyline.pointCount - 1{
            let point = self.point(for: self.polyline.points()[index])
            let path = CGMutablePath()


            currentColor = polyLine.getHue(from: index)

            if index == 0 {
               path.move(to: point)
            } else {
                let prevPoint = self.point(for: self.polyline.points()[index - 1])
                path.move(to: prevPoint)
                path.addLine(to: point)

                let colors = [prevColor!, currentColor!] as CFArray
                let baseWidth = self.lineWidth / zoomScale

                context.saveGState()
                context.addPath(path)

                let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: [0, 1])

                context.setLineWidth(baseWidth)
                context.replacePathWithStrokedPath()
                context.clip()
                context.drawLinearGradient(gradient!, start: prevPoint, end: point, options: [])
                context.restoreGState()
            }
            prevColor = currentColor
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用

CLLocations数组创建一行

let runRoute = GradientPolyline(locations: locations)
self.mapView.addOverlay(runRoute)
Run Code Online (Sandbox Code Playgroud)

在委托中传递 GradientPolylineRenderer

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is GradientPolyline {
        let polyLineRender = GradientMKPolylineRenderer(overlay: overlay)
        polyLineRender.lineWidth = 7
        return polyLineRender
    }
}
Run Code Online (Sandbox Code Playgroud)

结果

渐变折线


Coc*_*ics 1

在我看来,线条绘图视图中的drawRect缺少渐变的设置。绘制可能发生在覆盖层的不同线程上。请发布代码。