redshift5
redshift5

Reputation: 2006

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?

Upvotes: 12

Views: 8613

Answers (3)

Skotch
Skotch

Reputation: 3091

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

Upvotes: 19

y0gie007
y0gie007

Reputation: 727

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

mapViewDidFinishRenderingMap:fullyRendered

Upvotes: 5

Omar
Omar

Reputation: 463

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.

Upvotes: 0

Related Questions