7

Since - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView is not called when the tiles are loaded from cache, is there a way to know when all the tiles have been loaded (either from cache or from the mapping servers) and displayed?

Is there any delegation that intimates that tiles have been loaded ?

Kuldeep
  • 2,589
  • 1
  • 18
  • 28
Iñigo Beitia
  • 6,303
  • 4
  • 40
  • 46
  • 2
    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:27

2 Answers2

4

Here is some source code I wrote: https://github.com/jonathanlking/mapViewTest

Why don't you think about the problem like this;

When the map view will change, mapView:regionDidChangeAnimated: will be called.

From there mapViewWillStartLoadingMap: will be called.

Next mapViewDidFailLoadingMap:withError: or mapViewDidFinishLoadingMap: will be called if the tiles have been fetched from the server.

However if neither are called, you can assume the tiles are being loaded from the cache.

Jonathan King
  • 1,528
  • 14
  • 25
  • 2
    Thanks but there is still no way to know when they are displayed. – Iñigo Beitia Aug 08 '12 at 18:47
  • They are displayed instantly - as they are cached. – Jonathan King Aug 08 '12 at 18:53
  • 2
    Sorry, but this does not work. I removed the internet connection on my iPod Touch and zoomed to a level where there were no cached tiles and it still states that the map finished loading successfully (from cache in your example). This is a bug on Apple's part. – iwasrobbed May 01 '13 at 13:11
2

As mentioned, mapViewDidFinishLoadingMap 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 at the last call all of the tiles are rendered. So I think you can get this to work if you set up a 2 second timer after the map starts changing. Disable interactions so that the map does not continue to change, and enable user interactions when the timer goes off.

If mapViewDidFinishLoadingMap gets called reset the timer again for 2 seconds into the future. When the timer finally goes off, you should have a fully rendered map.

You will 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)mapLoadingIsFinished
{
    self.finishLoadingTimer = nil;
    self.mapChanging = NO;
    self.view.userInteractionEnabled = YES;
}

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    if (self.mapChanging) {
        [self restartTimer];
    }
}

- (void)startLookingForMapChange
{
    assert(self.mapChanging == NO);
    if (self.mapChanging == NO) {
        self.mapChanging = YES;
        assert(self.finishLoadingTimer == nil);
        self.view.userInteractionEnabled = NO;
        [self restartTimer];
    }
}
Skotch
  • 3,072
  • 2
  • 23
  • 43
  • This doesn't work for the scenario where you've lost internet connection so it can't load more tiles. It still calls the delegate saying that the map has fully loaded. – iwasrobbed May 01 '13 at 13:12