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 缩放或平移路径时(如果没有它们,如果我只是作为用户这样做),路径是锯齿状的,如下面的屏幕截图所示:

截屏

我测试了切换到 MKOverlayLevelAboveLabels 是否会有所不同,但遗憾的是结果是一样的。

有没有人有关于如何改进渲染的建议?切换到测地线路径是否会有所不同,如果是这样,我将如何在这里实现?

4

6 回答 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;
}
于 2015-08-25T07:55:31.733 回答
2

一旦在地图上绘制线,如果用户缩放,它可能不会重新渲染。或者,如果是这样,它可能会在用户完成缩放之前重新渲染。在这种情况下,缩放后的宽度将不再反映您想要的宽度(以米为单位)。处理此问题的一种方法是覆盖 regionDidChangeAnimated 并删除覆盖并将其添加回来。

于 2014-09-09T08:16:57.847 回答
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 个滴答声并不会杀死 CPU 并告别锯齿形。

于 2017-02-03T03:04:13.570 回答
2

MKPolylineRenderer 严重损坏,因为它不会在屏幕外重绘,并且计算其 cliprect 的逻辑错误,这会导致端盖伪影留在屏幕上。删除和阅读覆盖对我没有任何帮助。尝试修复线宽确实有效,但您仍然会遇到线宽较大的端盖问题。使用 roadSizeForZoomLevel 选项也不起作用(lineWidth = 0)

为了摆脱永远不会消失的端盖伪影,我使用了 Breadcrumb 示例应用程序中的渲染器。现在我只是在移动地图时偶尔会遇到无法接受的重绘问题。

我认为面包屑渲染器是 PolylineRenderer 应该是的,但有人破坏了它。但即使仍然不清楚如何强制屏幕外重绘(我不是核心图形专家,但鉴于苹果地图应用程序没有表现出这种行为,我相信大师可以解决这个问题。

无论如何,如果您至少想要一个不会在屏幕上留下垃圾的渲染器,请使用面包屑渲染器。那是我能找到的最好的。如果您真的需要更好的地图包,请尝试 googmaps

于 2016-12-18T14:37:21.667 回答
1

斯威夫特 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)
        }
    }

}

然后在您的 rendererFor MapKit 委托中使用它:

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = CustomPolyline(overlay: overlay)
        renderer.strokeColor = UIColor.red
        renderer.lineWidth = 100
        return renderer
}

缩放后您的折线不会重新渲染,从而避免了伪影

于 2017-07-25T08:43:53.907 回答
0

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

只需添加您的路径,而不是动态地向 CrumpPath 添加点。

其次,既然您已经修复了 MKPolylines 错误渲染,您需要加快它,因为它非常慢。

请参阅有关堆栈溢出的此答案:https ://stackoverflow.com/a/28964449/7313127

要使其适应“CrumbPathRenderer”,只需将此 obj C 代码添加到 drawMapRect 函数(这既快又脏)

静态 dispatch_once_t 一次令牌;

dispatch_once(&onceToken, ^{

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

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

在调用 setNeedsDisplay 的渲染器上创建更新方法

-(无效)更新{

[self setNeedsDisplay];

}

我还将 renderer.setNeedsDisplay 放入(但可能不需要)

func mapView(_ mapView: MKMapView, regionWillChangeAnimated 动画:Bool)

{

 crumbRenderer.setNeedsDisplay()

}

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

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

嘿苹果,想修复 MKMapView 吗?

于 2016-12-18T16:10:13.847 回答