12

I'm attempting to save a thumbnail of a mapview when a user taps save when an annotation has been selected. The problem occurs when the user has not zoomed in on that annotation yet, so the close zoom level has not been loaded.

This is what I'm doing after the user taps save:

  1. Set a bool "saving" to true
  2. Center and zoom in on the annotation (no animation)
  3. When the mapViewDidFinishLoadingMap delegate method gets called, and if saving is true:
  4. Create an UIImage out of the view, and save it. Dismiss modal view.

However when the image is saved, and the view is dismissed the result image saved actually has not finished loading, as I still see an unloaded map with gridlines as shown below:

Gridlines unfinished

My question is, how can I ensure the map is finished loading AND finished displaying before I save this thumbnail?

redshift5
  • 1,966
  • 1
  • 18
  • 27
  • I have filed Apple Bug #13774496 related to this and created an example app to show the issue still exists on iOS6: https://github.com/iwasrobbed/MapKitDelegateBug – iwasrobbed May 01 '13 at 15:32

3 Answers3

19

Update: iOS7 has a new delegate which may have fixed this problem. I have not confirmed one way or the other yet.

- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered 

Pre iOS6 support:

mapViewDidFinishLoadingMap: appears to be unreliable. I notice that it is sometimes not called at all, especially if the map tiles are already cached, and sometimes it is called multiple times.

I notice that when it is called multiple times the last call will render correctly. So I think you can get this to work if you set up a 2 second timer after the user taps save. Disable interactions so that nothing else can happen, and enable user interactions when the timer goes.

If mapViewDidFinishLoadingMap gets called reset the timer again for 2 seconds in the future. When the timer finally goes off, get the snapshot of the map and it should be correct.

You will also want to consider the other callbacks such as mapViewDidFailLoadingMap. Also test this on a noisy connection, since 2 seconds may not be long enough if it takes a long time to fetch the tiles.

- (void)restartTimer
{
    [self.finishLoadingTimer invalidate];
    self.finishLoadingTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                               target:self
                                                             selector:@selector(mapLoadingIsFinished)
                                                             userInfo:nil
                                                              repeats:NO];
}

- (void)userClickedSave
{
    assert(self.saving == NO);
    if (self.saving == NO) {
        self.saving = YES;
        assert(self.finishLoadingTimer == nil);
        self.view.userInteractionEnabled = NO;
        [self restartTimer];
    }
}

- (void)mapLoadingIsFinished
{
    self.finishLoadingTimer = nil;
    [self doSnapshotSequence];
    self.saving = NO;
    self.view.userInteractionEnabled = YES;
}

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    if (self.saving) {
        [self restartTimer];
    }
}
Community
  • 1
  • 1
Skotch
  • 3,072
  • 2
  • 23
  • 43
  • The above code will call mapLoadingIsFinished: method for only two times when the user tap save and in mapViewDidFinishLoadingMap: method,if saving is YES.. But in that Question, he mentioned "he still see an unloaded map with gridlines even after mapViewDidFinishLoadingMap: method called" ..I dont think this will work in anyway – Dinesh Raja Jan 19 '13 at 21:07
  • @RA As I mention above, mapLoadingIsFinished is called multiple times and the last time it is called it is reliable. This code takes advantage of that. Please try the code and let me know if you see any problems. – Skotch Jan 21 '13 at 18:09
5

If developing for iOS7 the best delegate to use is: mapViewDidFinishRenderingMap:fullyRendered:

mapViewDidFinishRenderingMap:fullyRendered

y0gie007
  • 727
  • 1
  • 7
  • 11
0

Are you sure the area where you are taking the screenshot has the Zoom Level supported which you are applying. For example, in US zoom level support is higher, you can zoom in to the maximum detail, while in Asia may be a high zoom level might not be supported.

Omar
  • 463
  • 3
  • 11