MKPolylineRenderer 产生了锯齿状、不均匀的路径

9

我正在使用iOS 7 MapKit API在地图上显示MKDirectionsRequest生成路径,并产生3D相机动画效果。MKOverlayRenderer将路径渲染如下:

-(void)showRoute:(MKDirectionsResponse *)response
{
for (MKRoute *route in response.routes)
 {
    [self.map
     addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
 }
}

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay
{
 MKPolylineRenderer *renderer =
 [[MKPolylineRenderer alloc] initWithOverlay:overlay];
UIColor *mapOverlayColor = [UIColor colorWithRed:((float)22 / 255.0f) green:((float)126 / 255.0f) blue:((float)251 / 255.0f) alpha:0.8];
 renderer.strokeColor = mapOverlayColor;
 renderer.lineWidth = 13.0;
 return renderer;
}

除一个问题外,一切都运行良好。当我使用MKMapCameras缩放或平移路径(如果我只是作为用户这样做),路径会呈现出如屏幕截图中所示的锯齿状:

screen shot

我测试了一下是否将其转换为MKOverlayLevelAboveLabels能有所改善,但遗憾的是结果仍然相同。

有没有人有任何建议来改善渲染效果?转换为大地线路径是否有所不同,如果是,我应该如何在这里实现?


切换到 MKGeodesicPolyline 不会有任何差别,但是它的创建方式与 MKPolyline 相同,只需在 MKGeodesicPolyline 上调用 polylineWithXXX。然而,渲染器仍为 MKPolylineRenderer。 - user467105
6个回答

3

子类化MKPolylineRenderer并重写applyStrokePropertiesToContext:atZoomScale:方法,以便忽略比例尺,在恒定宽度下绘制线条:

@interface ConstantWidthPolylineRenderer : MKPolylineRenderer
@end

@implementation ConstantWidthPolylineRenderer

- (void)applyStrokePropertiesToContext:(CGContextRef)context
                           atZoomScale:(MKZoomScale)zoomScale
{
    [super applyStrokePropertiesToContext:context atZoomScale:zoomScale];
    CGContextSetLineWidth(context, self.lineWidth);
}

@end

现在使用它并欣赏其平滑渲染:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    MKPolyline *polyline = (MKPolyline *)overlay;
    ConstantWidthPolylineRenderer *renderer = [[ConstantWidthPolylineRenderer alloc] initWithPolyline:polyline];
    renderer.strokeColor = [UIColor redColor];
    renderer.lineWidth = 40;
    return renderer;
}

1
这个解决方案将只有静态行宽,因此与缩放相关的问题会出现-当我们进行缩放/捏时,线条在某个级别后将不可见。 - Vijay Sharma

2

一旦在地图上绘制了线条,如果用户缩放地图,则该线条可能不会重新渲染。或者,如果它重新渲染,则可能会在用户完成缩放之前重新渲染。在这种情况下,缩放后的宽度将不再反映您期望的米宽度。解决此问题的一种方法是覆盖regionDidChangeAnimated并删除叠加层,然后再添加回来。


2
MKPolylineRenderer存在严重问题,因为它不会重新绘制屏幕外的内容,并且它在计算其剪辑区域时有错误的逻辑,导致拐角处的瑕疵留在屏幕上。删除和重新添加覆盖层对我没有任何作用。尝试修复线宽确实有效,但是使用较大的线宽仍然会出现拐角问题。使用“roadSizeForZoomLevel”选项也行不通(线宽=0)。
为了消除永远不会消失的端点瑕疵,我使用了Breadcrumb示例应用程序中的渲染器。现在,当移动地图时,我只偶尔会遇到无法接受的重绘问题。
我认为Breadcrumb渲染器就是PolylineRenderer应该成为的东西,但是有人破坏了它。即使如此,仍然不清楚如何强制离屏重绘(我不是核心图形专家,但鉴于苹果地图应用程序不表现出这种行为,我相信一个大师可以解决这个问题)。
总之,如果您至少想要一个不会在屏幕上留下垃圾的渲染器,请使用Breadcrumb渲染器。那是我能找到的最好的。如果您真的需要更好的MapKit,请尝试使用Google Maps。

2

当缩放级别或区域发生变化时,MKPolyline无法绘制。下面是简单的修复方法。

public class PolylineRenderer : MKPolylineRenderer {

    private var displayLink: CADisplayLink!
    private var ticks = 0

    override public init(overlay: MKOverlay) {
        super.init(overlay: overlay)

        displayLink = CADisplayLink(target: self, selector:#selector(PolylineRenderer._update))
    displayLink.add(to: .main, forMode: .commonModes)
    }

    func _update() {
        if ticks < 3 {
            ticks+=1
        } else {
            ticks = 0
        }

        if ticks == 0 {
            self.setNeedsDisplay()
        }
    }

    deinit {
        if displayLink != nil {
            displayLink.invalidate()
        }
    }
}

一旦你意识到问题不在于绘制速度不够快,而是需要跳过3个时钟周期,这个问题就变得相当简单了。跳过3个时钟周期并不会使CPU崩溃,同时也能够消除锯齿状的图像。


1

Swift 3 解决方案:

创建一个 MKPolylineRenderer 的子类。

class CustomPolyline: MKPolylineRenderer {

    override func applyStrokeProperties(to context: CGContext, atZoomScale zoomScale: MKZoomScale) {
        super.applyStrokeProperties(to: context, atZoomScale: zoomScale)
        UIGraphicsPushContext(context)
        if let ctx = UIGraphicsGetCurrentContext() {
            ctx.setLineWidth(self.lineWidth)
        }
    }

}

然后在您的MapKit委托中使用它的rendererFor方法:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = CustomPolyline(overlay: overlay)
        renderer.strokeColor = UIColor.red
        renderer.lineWidth = 100
        return renderer
}

在缩放后,您的折线不会重新呈现,从而避免了伪像。


0

好的,我已经解决了MKPolylineRenderer渲染缓慢的问题。首先使用来自[苹果面包屑示例][1]的Breadcrumb渲染器https://developer.apple.com/library/content/samplecode/Breadcrumb/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010048-Intro-DontLinkElementID_2

简单地说,只需将路径添加到CrumpPath而不是动态添加点。

其次,现在您已经修复了MKPolylines错误的渲染,需要加速它,因为它非常缓慢。

请参阅stackoverflow上的这个答案:https://stackoverflow.com/a/28964449/7313127

要将此适应于“CrumbPathRenderer”,只需将此obj C代码添加到drawMapRect函数中(这只是快速且肮脏的方法)

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];

    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
});

在渲染器上创建一个更新方法,调用setNeedsDisplay函数。
-(void) update {
[self setNeedsDisplay];

}

我也在其中加入了 renderer.setNeedsDisplay(但可能不需要)

func mapView(_ mapView: MKMapView,regionWillChangeAnimated animated:Bool)

{

 crumbRenderer.setNeedsDisplay()

}

重要提示:这将完美地呈现,但会使用100%的CPU。因此,为了不耗尽手机电池并且不损坏CPU,在更新方法中保持静态,并仅在显示链接调用它的第三次调用setNeedsDisplay。请记住,CA显示链接是硬件刷新计时器。

如果您遵循这个(匆忙撰写的)答案,我花了几天时间才弄清楚,您将使用约30%的CPU,并且您的地图路径永远不会变得丑陋。

嘿,苹果,想修复MKMapView吗?


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