如何自定义 MKPolyLineView 绘制不同风格的线条

Posted

技术标签:

【中文标题】如何自定义 MKPolyLineView 绘制不同风格的线条【英文标题】:how to customize MKPolyLineView to draw different style lines 【发布时间】:2011-10-14 12:28:35 【问题描述】:

我想自定义在 MKMapView 上绘制的线条以显示路线,以便线条具有边框颜色和填充颜色。与此类似,它有一个黑色边框并用另一种颜色填充:

我目前只是从mapView:viewForOverlay: 返回 MKPolyLineView 对象,这对于纯线效果很好。文档说 MKPolyLineView 不能被子类化,所以我应该将 MKOverlayView 子类化并实现我自己的 drawMapRect 吗?或者我应该继承 MKOverlayPathView?或者为 MKPolylineView 创建一个替代品?

编辑 - 我要问的是:放置你自己的 Quartz 绘图代码以绘制你自己的注释/覆盖的地方在哪里?目前我已经创建了一个 MKOverlayView 的子类并实现了我自己的 drawMapRect:zoomScale:inContext: 以这种方式绘制叠加层非常容易,但这是最好的解决方案吗?

【问题讨论】:

【参考方案1】:

您可以通过实现自己的 MKOverlayPathView 子类来做到这一点,该子类在地图矩形中绘制路径两次。一次用黑色加厚,一次用另一种颜色在上面变薄。

我创建了一个简单的 MKPolylineView 替代品,让您可以这样做:ASPolylineView。

如果您想自己做,您需要实现的两个主要方法可能如下所示:

- (void)drawMapRect:(MKMapRect)mapRect
          zoomScale:(MKZoomScale)zoomScale
          inContext:(CGContextRef)context

    UIColor *darker = [UIColor blackColor];
    CGFloat baseWidth = self.lineWidth / zoomScale;

    // draw the dark colour thicker
    CGContextAddPath(context, self.path);
    CGContextSetStrokeColorWithColor(context, darker.CGColor);
    CGContextSetLineWidth(context, baseWidth * 1.5);
    CGContextSetLineCap(context, self.lineCap);
    CGContextStrokePath(context);

    // now draw the stroke color with the regular width
    CGContextAddPath(context, self.path);
    CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
    CGContextSetLineWidth(context, baseWidth);
    CGContextSetLineCap(context, self.lineCap);
    CGContextStrokePath(context);

    [super drawMapRect:mapRect zoomScale:zoomScale inContext:context];


- (void)createPath

    // turn the polyline into a path

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

    for (int i = 0; i < self.polyline.pointCount; i++) 
        CGPoint point = [self pointForMapPoint:self.polyline.points[i]];

        if (pathIsEmpty) 
            CGPathMoveToPoint(path, nil, point.x, point.y);
            pathIsEmpty = NO;
         else 
            CGPathAddLineToPoint(path, nil, point.x, point.y);
        
    

    self.path = path;

【讨论】:

【参考方案2】:

您可以只添加两个坐标相同但粗细不同的 MKPolyLineView 对象。

添加一个 lineWidth 为 10(或其他)的,strokeColor 设置为黑色。

然后添加另一个 lineWidth 为 6 的 strokeColor 设置为您想要的其他颜色。

您可以对两个 MKPolyLineView 对象使用相同的 MKPolyLine。

【讨论】:

嗯,好主意,只需确保较宽的折线位于较细的折线下方。 好主意,也许您可​​以将较细的折线添加为较宽的折线的子视图?由于 MKPolyLineView 从 UIView 扩展而来,这在理论上是可能的...... 你可以这样做,但是你需要偏移子视图的坐标,这意味着不共享 MKPolyLine。如果您希望它们一起转换,最好让它们成为父 UIView 对象的子对象。【参考方案3】:

MKPolylineView 只能用于描边指定路径。您可以使用MKOverlayPathView 中的一些属性来更改它们的外观,但只有其中一些会适用,例如fillColor, strokeColor.

如果你想画更复杂的东西,你可以使用MKOverlayPathView。它更通用,因此不仅适用于抚摸路径。对于绘制简单的线条,结果将与MKPolylineView 相同(至少,根据文档)。

如果你想做更复杂的绘图,子类MKOverlayPathView。您正在尝试做的事情并非微不足道。

【讨论】:

【参考方案4】:

我使用了一个名为 NamedOverlay 的子类,它包含一个覆盖层和一个名称:

NamedOverlay.h

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface NamedOverlay : NSObject <MKOverlay>

@property (strong, readonly, nonatomic) NSString *name;
@property (strong, readonly, nonatomic) id<MKOverlay> overlay;

-(id)initWithOverlay:(id<MKOverlay>)overlay andName:(NSString *)name;

@end

NamedOverlay.m

#import "NamedOverlay.h"

@implementation NamedOverlay

- (id)initWithOverlay:(id<MKOverlay>)overlay andName:(NSString *)name

    _name = name;
    _overlay = overlay;
    return self;


- (MKMapRect)boundingMapRect

    return [_overlay boundingMapRect];


- (CLLocationCoordinate2D)coordinate

    return [_overlay coordinate];


-(BOOL)intersectsMapRect:(MKMapRect)mapRect

    return [_overlay intersectsMapRect:mapRect];


@end

在地图控制器中,我实例化了两个具有不同名称的叠加层,然后在 MKMapViewDelegate 中,我可以识别出我想要绘制的叠加层并执行以下操作:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id < MKOverlay >)overlay

    NamedOverlay *namedOverlay = (NamedOverlay *) overlay;
    MKPolyline *polyline = namedOverlay.overlay;
    if ([namedOverlay.name isEqualToString:@"top"]) 
        MKPolylineView *view1 = [[MKPolylineView alloc] initWithOverlay:polyline];
        view1.strokeColor = [UIColor whiteColor];
        view1.lineWidth = 25.0;
        return view1;
     else 
        MKPolylineView *view1 = [[MKPolylineView alloc] initWithOverlay:polyline];
        view1.strokeColor = [UIColor blueColor];
        view1.lineWidth = 15.0;
        return view1;
    

【讨论】:

【参考方案5】:

我知道这可能与您想要的纯方法不匹配,但为什么不使用 MKPolygon 而不是 MKPolyLine 呢? 创建一个MKPolygon 实例,它代表一种您的路线周围的走廊,然后,当您创建与您的MKPolygon/corridor 相对应的MKPolygonView创建后,设置MKPolygonView的属性,得到不同的填充颜色和描边颜色

  myPolygonView.lineWidth=3;
  myPolygonView.fillColor=[UIColor blueColor];
  myPolygonView.strokeColor=[UIColor darkGrayColor];

我自己没有尝试过,但这应该可以。唯一的缺点是当你放大/缩小时,'路线'的'宽度'会改变......:/

【讨论】:

好主意,但是从任意一组路线坐标计算多边形的边界非常复杂 一种基本的做法是,走廊上部的纬度加0.000001,下部的纬度加-0.000001,然后用上下两部分组成的多边形构建走廊。 ..只是一个想法... 这只有在路线沿东/西方向运行时才有效。这些可以在任何方向上运行,因此多边形需要基于每条线段的方向。侧面需要与路线航向成直角,并且使用 Spherical Law of Cosines 从纬度/经度对计算航向是一项昂贵的操作。 在 GIS 中,这可以使用 ST_BUFFER postgis.org/docs/ST_Buffer.html 来实现。如果你的 iPhone 上有 spaceite,你也可以通过 1 次调用来计算它:gaia-gis.it/spatialite-2.4.0-4/spatialite-sql-2.4-4.html。但我同意,这是一项昂贵的手术。

以上是关于如何自定义 MKPolyLineView 绘制不同风格的线条的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中使用 MKPolylineView

添加两个 MKPolylineView

在 MKPolyLine 视图中绘制渐变

如何在绘制之前获取调整大小的自定义视图的宽度和高度

我想在 UIView 上绘制像苹果形状这样的自定义形状

不同 API 上的奇怪自定义 SeekBar 可绘制行为