mapView addOverlay not always calling renderForOverlay

I am rendering state counties for a selected US state, by creating a polygon and filling in the color based on data values. The first time it does this for any given state (drawing the county outline, etc.) it is fine. Selecting a different state also works usually without issue.


When selecting the first state for the first time, here is the code flow:


2016-01-27 08:15:47.287 Otezla Dashboard[13493:4733614] drawCounties - county: MARION

2016-01-27 08:15:47.287 Otezla Dashboard[13493:4733614] rendererForOverlay - county: MARION

2016-01-27 08:15:47.287 Otezla Dashboard[13493:4733614] drawCounties - county: MANATEE

2016-01-27 08:15:47.288 Otezla Dashboard[13493:4733614] rendererForOverlay - county: MANATEE

2016-01-27 08:15:47.288 Otezla Dashboard[13493:4733614] drawCounties - county: GILCHRIST

2016-01-27 08:15:47.288 Otezla Dashboard[13493:4733614] rendererForOverlay - county: GILCHRIST


When selecting the first state again, the behavior is such:


2016-01-27 08:15:51.740 Otezla Dashboard[13493:4733614] drawCounties - county: MARION

2016-01-27 08:15:51.740 Otezla Dashboard[13493:4733614] drawCounties - county: MANATEE

2016-01-27 08:15:51.740 Otezla Dashboard[13493:4733614] drawCounties - county: GILCHRIST

and then

2016-01-27 08:15:51.853 Otezla Dashboard[13493:4733614] drawCounties - county: MONROE

2016-01-27 08:15:51.853 Otezla Dashboard[13493:4733614] drawCounties - county: HERNANDO

2016-01-27 08:15:51.853 Otezla Dashboard[13493:4733614] drawCounties - county: INDIAN RIVER

2016-01-27 08:15:51.854 Otezla Dashboard[13493:4733614] rendererForOverlay - county: INDIAN RIVER

2016-01-27 08:15:51.854 Otezla Dashboard[13493:4733614] rendererForOverlay - county: INDIAN RIVER

So basically, the 2nd time in, when viewForOverlay is called, renderForOverlay is not called until all counties in Florida are looped through the call to viewForOverlay, then it is as though all those requests were queued up and then renderForOverlay is called all x many times for the county, but it only uses the last county name cached. Thus, it is not view and render but view view view ... render render render.

Here is the view code:

  MKPolygon *polygon = [MKPolygon polygonWithCoordinates:coordinates count:coords.count];
  polygon.title      = currentCounty;

  [mapCountyOverlays setObject:polygon forKey:key];
  [mapView addOverlay:polygon];


and the render code:


- (MKOverlayView *) mapView :(MKMapView *) mapViewSent rendererForOverlay:(nonnull id<MKOverlay>)overlay
{
NSLog(@"rendererForOverlay - county:  %@", currentCounty);
return [self buildPolygonForMap:mapViewSent viewForOverlay:overlay];
}

/
* build polygon
*/
- (MKPolygonView *) buildPolygonForMap :(MKMapView *) mapView viewForOverlay :(id) overlay
{
readyToRender = NO;

MKPolygonView *polygonView = [[MKPolygonView alloc] initWithPolygon:overlay];

polygonView.strokeColor = [UIColor blackColor];
polygonView.lineWidth = 1.5;                          


if(stateRecentlySelected)                             
{
  polygonView.fillColor = [self colorizeState];
}
else                                                  
{
  if(currentCounty != nil)
  {
   NSNumber *tier = [countiesDistributed objectForKey:currentCounty];
  
   if(tier == nil)
   {
    return polygonView;
   }
  
   polygonView.strokeColor = [UIColor whiteColor];
   polygonView.fillColor = [self colorizeCounty :[tier intValue]];
  }
  else
  {
   NSLog(@"currentCounty is nil");
  }
}

readyToRender = YES;

[mapView setNeedsDisplay];

return polygonView;
}


Thanks.

Accepted Answer

You shouldn't assume anything about the order (or number) of calls made by MapKit. In general, it should work like this: the overlay object represents the data to be displayed and the state of the overlay - selected, highlighted or whatever. The renderer takes that state and turns it into drawing commands. So you should make an overlay subclass containing the additional state you need (whether your polygon is "current", "recently selected" or whatever makes sense in your app) and the renderer should ask its overlay (it has an "overlay" property) for the current state and use that to do the drawing (set colors and draw things). It's up to you whether your "state" stored in the overlay should include specifics such as the specific UIColor, or whether it should be more abstract. But the main thing is you have to store the details in the overlay and the renderer should query the overlay if and when rendering occurs.


If the state can change over time, such as by the user selecting things or moving or whatever, then you might need to redraw the overlay if the state changes. I have had good results by keeping a reference to the overlay renderer in the overlay subclass and calling setNeedsDisplay on the renderer if necessary when the state changes. That way your view controller knows nothing about the details of fill and stroke colors etc. and just sets the state of the overlay.


In any case, your code is not correct. -rendererForOverlay should return an MKOverlayRenderer, not a MKOverlayView. You should be using MKPolygonRenderer.

Thank you junkpile ... I did see that there is a title property for the polygon and it was set to the county name. By using that name when the renderer decided to fire fixed it !


I did not change the return type to MKOverlayRenderer, what is the general reason to return than instead of the MKOverlayView ?

mapView addOverlay not always calling renderForOverlay
 
 
Q