SetNeedsDisplayInMapRect does not call new drawMapRect: call

I use custom MKOverlay/MKOverlayView to completely cover the Google MKOverlay/MKOverlayView my own fragments that load asynchronously. I follow the unloaded slab request pattern when I get a call to canDrawMapRect:zoomScale: to my overlay view (and in this case return FALSE) and then calling setNeedsDisplayInMapRect:zoomScale: after it is available.

This generally works and seems to work great in a simulator.

However, on the device I sometimes get a β€œhole” in the overlay - a missing fragment.

I see that the fragment is being requested and the request is being completed. I see what I call setNeedsDisplayInMapRect:zoomScale: and pass in the original MKMapRect and MKZoomScale that were provided in canDrawMapRect:zoomScale: But I also see that the overlay is never asked to redraw this tile (none of canDrawMapRect:zoomScale: and drawMapRect:zoomScale:inContext: again called for this tile).

I need to understand why this is happening and how to fix it.

Here is the relevant code from my subclass of 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 sharedCache]; [tileRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; if ( NO == [[ASIDownloadCache sharedCache] 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 sharedCache] 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 : I guess this is most likely a problem.

+8
ios mapkit mkmapview mkoverlay
source share
2 answers

I had a problem very similar to that described here. In my case, I could not reproduce the desired behavior (described in http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKOverlayView_class/Reference/Reference.html#//apple_ref/occ/instm/MKOverlayView/ setNeedsDisplayInMapRect: zoomScale :), even having the simplest code:

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

or closer to my source code:

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

In both cases, setNeedsDisplayInMapRect: zoomScale: is never called even once.

The situation changed when I started running setNeedsDisplayInMapRect: zoomScale: inside dispatch_async sent to the same queue on which canDrawMapRect is running, for example:

 - (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; } 

or with asynchronous task enabled:

 - (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; } 

Using dispatch_async - I see an endless chain of "This should trace forever". My original problem also completely disappeared.

LATER UPDATE: I am currently using dispatch_get_main_queue () to call 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; } 
+1
source share

The answer above did not help me. From the NSLog listing, I used to see that a different thread was being used, despite capturing dispatch_get_current_queue () in canDrawMapRect and saving it for later use. So it was in the simulator iPad 4.3 at least I did not try on the device.

My solution was a less satisfactory and more error prone wait x time solution before the call.

 -(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]; }); } 
0
source share

All Articles