setNeedsDisplayInMapRect no activa new drawMapRect: call

Estoy usando un MKOverlay/MKOverlayView para cubrir completamente el map base de Google con mis propios mosaicos, que se cargan de forma asíncrona. Sigo el patrón de request de mosaicos descargados cuando recibo un canDrawMapRect:zoomScale: llama a mi vista de superposition (y devuelve FALSE en ese caso), luego llama a setNeedsDisplayInMapRect:zoomScale: una vez que el mosaico está disponible.

Todo esto generalmente funciona, y parece funcionar perfectamente en el simulador.

Sin embargo, en el dispositivo a veces tengo un "agujero" en la superposition: una ficha faltante.

Puedo ver que se solicita el mosaico y que la request se completa. Puedo ver que llamo a setNeedsDisplayInMapRect:zoomScale: y que estoy pasando el MKMapRect original y MKZoomScale que se proporcionaron en canDrawMapRect:zoomScale: . Pero también puedo ver que nunca se pide a la superposition que canDrawMapRect:zoomScale: a dibujar ese mosaico (ni canDrawMapRect:zoomScale: ni drawMapRect:zoomScale:inContext: se drawMapRect:zoomScale:inContext: nuevamente ese mosaico).

Necesito entender por qué sucede esto y cómo corregirlo.

Aquí está el código relevante de mi subclass MKOverlayView:

 - (BOOL) canDrawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale { NSUInteger zoomLevel = [self zoomLevelForZoomScale:zoomScale]; CGPoint mercatorPoint = [self mercatorTileOriginForMapRect:mapRect]; NSUInteger tilex = floor(mercatorPoint.x * [self worldTileWidthForZoomLevel:zoomLevel]); NSUInteger tiley = floor(mercatorPoint.y * [self worldTileWidthForZoomLevel:zoomLevel]); NSURL* tileUrl = [self tileURLForZoomLevel: zoomLevel tileX: tilex tileY: tiley]; ASIHTTPRequest* tileRequest = [ASIHTTPRequest requestWithURL: tileUrl]; tileRequest.downloadCache = [ASIDownloadCache shanetworkingCache]; [tileRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; if ( NO == [[ASIDownloadCache shanetworkingCache] isCachedDataCurrentForRequest: tileRequest] ) { [tileRequest setCachePolicy: ASIAskServerIfModifiedWhenStaleCachePolicy]; tileRequest.delegate = self; tileRequest.userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSValue value: &mapRect withObjCType: @encode( MKMapRect )], @"mapRect", [NSValue value: &zoomScale withObjCType: @encode( MKZoomScale )], @"zoomScale", [NSNumber numberWithInt: tilex], @"tilex", [NSNumber numberWithInt: tiley], @"tiley", nil]; [_tileRequestStack addOperation: tileRequest]; NSLog( @"canDrawMapRect: %d, %d - REQUESTING", tilex, tiley ); return NO; } NSLog( @"canDrawMapRect: %d, %d - READY", tilex, tiley ); return YES; } - (void) drawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale inContext: (CGContextRef) context { NSUInteger zoomLevel = [self zoomLevelForZoomScale:zoomScale]; CGPoint mercatorPoint = [self mercatorTileOriginForMapRect:mapRect]; NSUInteger tilex = floor(mercatorPoint.x * [self worldTileWidthForZoomLevel:zoomLevel]); NSUInteger tiley = floor(mercatorPoint.y * [self worldTileWidthForZoomLevel:zoomLevel]); NSLog( @"drawMapRect: %d, %d", tilex, tiley ); NSURL* tileUrl = [self tileURLForZoomLevel: zoomLevel tileX: tilex tileY: tiley]; NSData* tileData = [[ASIDownloadCache shanetworkingCache] cachedResponseDataForURL: tileUrl]; UIGraphicsPushContext(context); if ( tileData != nil ) { UIImage* img = [UIImage imageWithData: tileData]; if ( img != nil ) { [img drawInRect: [self rectForMapRect: mapRect] blendMode: kCGBlendModeNormal alpha: 1.0 ]; } else { NSLog( @"oops - no image" ); } CGSize s = CGContextConvertSizeToUserSpace( context, CGSizeMake( 40, 1 )); UIFont* f = [UIFont systemFontOfSize: s.width]; [[UIColor blackColor] setFill]; [[NSString stringWithFormat: @"%d,%d", tilex, tiley] drawInRect: [self rectForMapRect: mapRect] withFont: f]; } UIGraphicsPopContext(); } - (void) requestFinished: (ASIHTTPRequest *) tileRequest { NSValue* mapRectValue = [tileRequest.userInfo objectForKey: @"mapRect"]; MKMapRect mapRect; [mapRectValue getValue: &mapRect]; NSValue *zoomScaleValue = [tileRequest.userInfo objectForKey:@"zoomScale"]; MKZoomScale zoomScale; [zoomScaleValue getValue: &zoomScale]; NSLog( @"requestFinished: %d, %d, %lf", [[tileRequest.userInfo objectForKey:@"tilex"] intValue], [[tileRequest.userInfo objectForKey:@"tiley"] intValue], zoomScale ); [self setNeedsDisplayInMapRect: mapRect zoomScale: zoomScale]; } 

EDIT : Supongo que este es probablemente el problema.

Tuve un problema muy similar al descrito aquí. En mi caso, no pude reproducir el comportamiento deseado (descrito en http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKOverlayView_class/Reference/Reference.html#//apple_ref/occ/instm/ MKOverlayView / setNeedsDisplayInMapRect: zoomScale 🙂 incluso teniendo el código más simple posible:

 - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { NSLog(@"This should trace forever"); [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; return NO; } 

o más cerca de mi código original:

 - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { NSLog(@"This should trace forever"); [SomeAsynchronousRequestWithCompletionHandler:^{ [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; }]; return NO; } 

En ambos casos, setNeedsDisplayInMapRect: zoomScale: nunca se ha llamado ni una sola vez.

La situación cambió cuando empecé a ejecutar setNeedsDisplayInMapRect: zoomScale: dentro de un dispatch_async enviado a la misma queue en la que se puede ejecutar canDrawMapRect, como:

 - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { dispatch_queue_t queue = dispatch_get_current_queue(); NSLog(@"This should trace forever"); dispatch_async(queue, ^{ [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; }); return NO; } 

o con trabajo asynchronous incluido:

 - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { NSLog(@"This should trace forever"); dispatch_queue_t queue = dispatch_get_current_queue(); [SomeAsynchronousRequestWithCompletionHandler:^{ dispatch_async(queue, ^{ [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; }); }]; return NO; } 

Utilizando dispatch_async, puedo ver la secuencia "Esto debería rastrear para siempre" rastreada sin fin. Mi problema original también desapareció por completo.

ACTUALIZACIÓN MÁS ADELANTE: Actualmente, uso dispatch_get_main_queue () para llamar a setNeedsDisplayInMapRect: zoomScale: like

 - (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale { NSLog(@"This should trace forever"); [SomeAsynchronousRequestWithCompletionHandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale]; }); }]; return NO; } 

La respuesta anterior no funcionó para mí. Desde la printing de NSLog que utilicé, pude ver que se estaba utilizando un hilo diferente a pesar de tomar el dispatch_get_current_queue () en canDrawMapRect y almacenarlo para su uso posterior. Este fue el caso en el iPad 4.3 Simulator al less, no intenté en el dispositivo.

Mi solución fue less satisfactoria y más solución propensa a errores de espera x time antes de llamar.

 -(void)setNeedsDisplay:(WebRequest*)webRequest { [webRequest retain]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [webRequest autorelease]; [delegate setNeedsDisplayInMapRect:webRequest.request.requestedMapRect zoomScale:webRequest.request.requestedScale]; }); }