Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use MKMapView finished loading delegate, possible "finished displaying" delegate?

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?

like image 512
redshift5 Avatar asked Oct 19 '12 03:10

redshift5


2 Answers

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];
    }
}
like image 90
Skotch Avatar answered Oct 31 '22 11:10

Skotch


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

mapViewDidFinishRenderingMap:fullyRendered

like image 5
y0gie007 Avatar answered Oct 31 '22 13:10

y0gie007