ios - MKPolylineRenderer produce caminos desiguales y desiguales
ios7 mapkit (6)
una vez que se dibuja la línea en el mapa, es posible que no se vuelva a renderizar si el usuario amplía. O, si lo hace, puede volver a procesarse antes de que el usuario termine de hacer zoom. En este caso, el ancho después del zoom ya no reflejará el ancho deseado en metros. Una forma de lidiar con esto es anular regionDidChangeAnimated y eliminar la superposición y volver a agregarla.
Estoy usando las API de iOS 7 MapKit para producir movimientos de cámara 3D en un mapa que muestra una ruta producida por MKDirectionsRequest. MKOverlayRenderer representa la ruta de la siguiente manera:
-(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;
}
Está funcionando bien, excepto por un problema. Cuando hago zoom o recorro la ruta con MKMapCameras (y sin ellos, si simplemente lo hago como usuario), la ruta se irregulariza como se muestra en esta captura de pantalla:
Probé para ver si cambiar a MKOverlayLevelAboveLabels hace una diferencia, pero lamentablemente el resultado fue el mismo.
¿Alguien tiene sugerencias sobre cómo mejorar el renderizado? ¿El cambio a una ruta geodésica marca la diferencia y, de ser así, cómo implementarlo aquí?
La subclase MKPolylineRenderer y reemplaza applyStrokePropertiesToContext: atZoomScale: para que ignore la escala y dibuje líneas con ancho constante:
@interface ConstantWidthPolylineRenderer : MKPolylineRenderer
@end
@implementation ConstantWidthPolylineRenderer
- (void)applyStrokePropertiesToContext:(CGContextRef)context
atZoomScale:(MKZoomScale)zoomScale
{
[super applyStrokePropertiesToContext:context atZoomScale:zoomScale];
CGContextSetLineWidth(context, self.lineWidth);
}
@end
Ahora úsalo y admira su renderizado sin problemas:
- (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;
}
MKPolylineRenderer está seriamente dañado ya que no volverá a dibujarse fuera de pantalla y tiene una lógica defectuosa para calcular su cliprect que causa que los artefactos de tapa final se dejen en la pantalla. Eliminar y leer la superposición no me sirvió de nada. Tratar de arreglar el ancho de la línea funciona, pero aún así tendrá problemas de tapa con anchos de línea más grandes. Usar la opción roadSizeForZoomLevel tampoco funcionará (lineWidth = 0)
Para deshacerse de los artefactos de tapa que nunca desaparecen, utilicé el procesador de la aplicación de muestra Breadcrumb. Ahora solo tengo el problema ocasionalmente inaceptable de volver a dibujar al mover el mapa.
El procesador de la barra de pan, creo, es lo que se suponía que era PolylineRenderer, pero alguien lo rompió. Pero aún no está claro cómo uno forzaría rediseños fuera de pantalla (no soy un experto en gráficos básicos, pero dado que la aplicación de mapas de manzanas no muestra este comportamiento, estoy seguro de que un gurú podría resolverlo).
De todas formas, si al menos quieres un procesador que no deje basura en la pantalla, utiliza el procesador de migas. Eso es lo mejor que pude encontrar. Si realmente necesitas un mejor juego de mapas, prueba googmaps
MKPolyline no está dibujando cuando el zoom está cambiando y cuando la región está cambiando. Solución simple a continuación.
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()
}
}
}
Es bastante simple una vez que te das cuenta de que no está pintando lo suficientemente rápido. Omitir 3 tics no mata a la CPU y adiós a los jaggies.
De acuerdo, solucioné este problema con lenta renderización de MKPolylineRenderer. Primero use el renderizador Breadcrumb de [Apple breadcrumb sample] [1] https://developer.apple.com/library/content/samplecode/Breadcrumb/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010048-Intro- DontLinkElementID_2
Simplemente, en lugar de agregar puntos dinámicamente a CrumpPath, solo agregue su ruta.
En segundo lugar, ahora que ha corregido el renderizado defectuoso de MKPolylines, necesita acelerarlo porque es terriblemente lento.
Ver esta respuesta en el desbordamiento de pila: https://.com/a/28964449/7313127
Para adaptar esto al "CrumbPathRenderer", simplemente agregue este código obj C a la función drawMapRect (Esto es solo rápido y sucio)
estático dispatch_once_t onceToken;
dispatch_once (& onceToken, ^ {}
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
});
Cree un método de actualización en el renderizador que llama a setNeedsDisplay
- (nulo) actualización {
[self setNeedsDisplay];
}
También pongo renderer.setNeedsDisplay (pero probablemente no sea necesario)
func mapView (_ mapView: MKMapView, regionWillChange animado animado: bool)
{
crumbRenderer.setNeedsDisplay()
}
NOTA IMPORTANTE: Esto se procesará sin problemas pero usará 100% de CPU. Por lo tanto, para no agotar la batería del teléfono y agitar la CPU, en el método de actualización mantenga una llamada estática y única setNeedsDisplay cada tres veces que el enlace de visualización lo llame. Recuerde que el enlace de visualización CA es un temporizador de actualización de hardware.
Si sigues esta respuesta (redactada apresuradamente) que me llevó varios días averiguar, utilizarás aproximadamente un 30% de CPU y las rutas de tu mapa nunca se verán feas.
Hey apple, ¿quieres arreglar MKMapView?
Solución Swift 3:
Crear una subclase de 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)
}
}
}
Luego, úselo en su renderizador para el delegado de MapKit:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = CustomPolyline(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 100
return renderer
}
Sus polilíneas no se volverán a renderizar después de hacer zoom, evitando así los artefactos