使用MapKit ios的渐变折线 [英] Gradient Polyline with MapKit ios

查看:96
本文介绍了使用MapKit ios的渐变折线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用叠加层(MKOverlay)跟踪MKMapView上的路线。
但是,根据当前的速度,如果颜色正在变化(例如,如果用户从65英里每小时开车到绿色变为橙色),我想在追踪路线时使用渐变来做类似Nike应用程序的操作30英里每小时。

I'm trying to trace a route on a MKMapView using overlays (MKOverlay). However, depending on the current speed, I want to do something like the Nike app with a gradient while tracing the route, if the color is changing (for example, from green to orange if a user is driving from 65mph to 30mph).

这是我想要的截图:

所以每隔20米我就会添加一个从旧到新的叠加层坐标使用:

So every 20 meters, I adding an overlay from the old to the new coordinates using:

// 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;

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

Basically I'm adding a lot of little overlays. Then I would like to create the gradient on this little line drawing, so I'm trying to do so in the overlay 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;
}

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

I'm trying to add the gradient this way, but I guess it is not the way to do it because I can't make it to work.

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

I can also trace the route every time there is an update on the user's location (in the location object's delegate), or as above every 20 meters.

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

Can you please help me on that one, giving me tips! Thanks!

推荐答案

我想出的一个想法就是创建一个CGPath并每次用渐变来抚摸它当 drawMapRect 方法被调用时,因为 MKPolylineView MKPlolylineRenderer 在ios7中。

One of the idea I came up is to create a CGPath and stroke it with gradient every time when drawMapRect method been called, since the MKPolylineView is replaced by MKPlolylineRenderer in ios7.

我试图通过继承 MKOverlayPathRenderer 来实现这个,但是我没有选择单独的CGPath ,然后我找到一个名为的神秘方法 - (void)strokePath:(CGPathRef)路径inContext:(CGContextRef)context 这听起来像我需要的,但它不会被调用如果你在覆盖 drawMapRect 时没有调用super方法。

I tried to implement this by subclassing a MKOverlayPathRenderer but I failed to pick out individual CGPath, then I find a mysterious method named-(void) strokePath:(CGPathRef)path inContext:(CGContextRef)context which sounds like what I need, but it will not be called if you don't call the super method when you override your drawMapRect.

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

thats what Im working out for now.

我会继续这样做,如果我找到了什么我会回来并更新答案。

I'll keep trying so if I work out something I'll come back and update the answer.

======的更新 ================================ ================

=========UPDATE================================================

这就是我最近解决的问题,我几乎实现了上面提到的基本思想但是,我仍然无法根据特定的mapRect选择单独的PATH,所以我只是在所有路径的boundingBox与当前mapRect相交时同时绘制所有具有渐变的路径。糟糕的伎俩,但现在工作。

So that is what I'm worked out these days, I almost implemented the basic idea mentioned above but yes, I still cannot pick out an individual PATH according to specific mapRect, so I just draw all paths with gradient at the same time when the boundingBox of all paths intersects with current mapRect. poor trick, but work for now.

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

In the -(void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context method in render class, I do this:

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;

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

Then I split the entire path point by point to draw its gradient individually. note that the hues array containing hue value mapping each velocity of location.

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];
}

这是所有核心绘图方法,当然你需要 velocity 在你的覆盖类中,用CLLocationManager提供它们。

That is all the core drawing method and of course you need points, velocity in your overlay class and feed them with CLLocationManager.

最后一点是如何得到 hue 超出速度的值,嗯,我发现如果0.03~0.3范围内的色调恰好代表从红色到绿色,那么我做一些按比例映射到色调和速度。

the last point is how to get hue value out of velocity, well, I found that if hue ranging from 0.03~0.3 is exactly represent from red to green, so I do some proportionally mapping to hue and velocity.

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

last of the last, here you are this is full source of this demo:https://github.com/wdanxna/GradientPolyline

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

don't panic if can't see the line you draw, I just position the map region on my position :)

这篇关于使用MapKit ios的渐变折线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆