22

I am aware of the delegate methods used to let me know when the map has loaded and annotations and overlays have been added. (mapViewDidFinishLoadingMap: mapView:didAddAnnotationViews: mapView:didAddOverlayViews:)

I am wanting to create a UIImage from my MKMapView once everything has loaded. Currently I am creating my UIImage once mapView:didAddOverlayViews: is called, but this is not always reliable, because sometimes the overlay take longer to be added, sometimes mapViewDidFinishLoadingMap: is called more than once or it takes a long time to load. Sometimes it is NOT called because tiles are cached. So, it is very hard to know exactly when everything has loaded. I have tried using a timer but that doesn't make it reliable either.

My question is, how can I know when everything has completely loaded, including all map tiles, all annotations and all overlays?

Nic Hubbard
  • 41,587
  • 63
  • 251
  • 412
  • 1
    I think you can try use a counter variable that starts form 0, I assume you know number of annotations to be added so you can compare.. The method - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation will get called from every annotation you will add increase the counter inside the method and if it reaches the total number of annotation call capture image function by performselector with some delay to be precisely perfect.. I think it should work.. – iphonic Jan 03 '13 at 08:47

6 Answers6

22

It's an old question, but if you're using iOS 7 just use the mapView mapViewDidFinishRenderingMap delegate.

- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered
{
    // Image creation code here

}
Peter Todd
  • 8,561
  • 3
  • 32
  • 38
5

The following code works on iOS 8.4 in Swift 1.2

func mapViewDidFinishRenderingMap(mapView: MKMapView!, fullyRendered: Bool) {
    // Your code on rednering completion
}
Luke Pearce
  • 133
  • 1
  • 3
1

The problem with mapViewDidFinishRenderingMap is that is only includes the loading of the map tiles themselves, not the tiles plus the annotations. For this, you can use didAddAnnotationViews. To be safe, I would recommend observing both, and using which ever returns later.

Ben Packard
  • 26,102
  • 25
  • 102
  • 183
1

Well, you could set three flags in your delegate:

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    didFinishLoadingMap = YES;
    [self createImage];
}

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
    didFinishAddingAnnotationViews = YES;
    [self createImage];
}

- (void)mapView:(MKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews

{
    didFinishAddingOverlayViews = YES;
    [self createImage];
}

...and then

- (void)createImage
{
    if (didFinishLoadingMap && didFinishLoadingAnnotationViews && didFinishAddingOverlayViews) {
        // Create the image
    }
}

...and then set all three flags back to NO in your mapViewWillStartLoadingMap: method. This might create an image slightly more often than necessary, in the scenario where map tiles are cached but both new overlays and new annotations scroll onto the screen – if you wanted to guard against that, you could use a UIPanGestureRecognizer to detect when the user pans or pinches the map, and reset those two flags to NO accordingly.

Scott Forbes
  • 7,397
  • 1
  • 26
  • 39
  • Yeah, I thought of doing this before. From what I have seen mapViewDidFinishLoadingMap: doesn't get called when loading cached tiles. So, this wouldn't be reliable. – Nic Hubbard Dec 19 '11 at 17:31
  • That's where the `UIPanGestureRecognizer` comes in, to reset the annotation and overlay flags to `NO` in the scenario where the map isn't loading tiles. There's probably still a problem, though: Unless your annotations and overlays are so thick on the ground that one of each gets added every time the user scrolls the map, you're not going to get all three flags set to `YES` every time… and testing whether the newly scrolled-into-view region contains overlays and annotations is too expensive to do on the fly, in my experience. – Scott Forbes Dec 19 '11 at 18:01
  • There is no panning on this `MKMapView`. I am creating a `UIImage` from it, that is why I need to know when everything is loaded so that I can get a nice image of the map with everything loaded. – Nic Hubbard Dec 19 '11 at 18:11
  • Hmm. In that case, shouldn't you be able to use the `mapViewWillStartLoadingMap:` method to detect when the map is downloading map tiles from the server? If `mapViewWillStartLoadingMap:` gets called, you need to set a flag and wait until `mapViewDidFinishLoadingMap:`; if `mapViewWillStartLoadingMap:` does *not* get called, then you're using cached tiles, and you don't need to wait. Setting the flag to YES initially, then setting it to NO in `mapViewWillStartLoadingMap:`, might do the trick. – Scott Forbes Dec 19 '11 at 18:31
  • 4
    I think that again, mapViewWillStartLoadingMap: will be unreliable because it won't get called if there are cached tiles. How would I know if mapViewWillStartLoadingMap: isn't going to be called, or if it is just taking a really long time for a slow connection? – Nic Hubbard Dec 19 '11 at 18:38
  • @NicHubbard hi Nic. Have you found a solution to this problem in which I am also involved?!..i've tried dozens of solutions but no one tells me when all the annotations are loaded..thanks. – Mat Nov 16 '12 at 00:47
  • Nope, never found a solution. Moved away from that project so I didn't end up needing one. – Nic Hubbard Nov 18 '12 at 23:11
  • This is hopeless... Atleast there should be a graceful way of knowing the map has been fully loaded either first time or cached tiles.. Come on Apple!! – holierthanthou84 Nov 20 '13 at 05:16
0

This is relatively easy if you get your mind out of the "map view". What I did with graphing something all at once was to put all my Requests into a total. I stored the total number of "requests" and when they finished their loading function, I had a check of "if requests left == 0" or similar, then I called the "Draw everything after".

So what you'd do is make the 3 separate types add to a "running total" (since a queue itself is different in Comp Sci, trying to avoid vocabulary conflict), then when they "run out", via having their "Draw rect" call or "init with coder" if it's a Xib annotation or whatever function you can guarantee since iOS UIView subclasses have absolutely no common initialization function like every other programming language (separate rant), basically that init calls a function on a common class, singleton, static int, delegate, whichever, and then see when it-runs out-.

I realize your project ended, but with so many upvotes, figured an answer wouldn't hurt. This answer would be much simpler if Objective C had a constructor. Either way, this should solve it, just takes some thought and time, as it's per-project on exactly how to implement it.

Stephen J
  • 2,367
  • 2
  • 25
  • 31
  • 1
    Im not sure what you are trying to say here. Are you checking if `drawRect:` has been called on each annotation? – Robert Mar 05 '13 at 17:17
  • No, I'm finding a common function that will always be called at least once, hopefully when the view is initialized, that lets the person know that it displayed on screen.
    When all queued views hit their initializer, you will know because that number will match the number of views. Then you can do the function. DrawRect: was probably not a good example, but IOS really has no common view initializer you can subclass for Xibs and Code together. Some come close, but not quite
    – Stephen J Mar 05 '13 at 21:25
  • Just a note, since this question has been downvoted, if you're downvoting because iOS 7 now has a solution, look at the date of this answer. If it still doesn't make sense, why are you a programmer.. – Stephen J Feb 17 '14 at 03:22
0

I have tried all ways from other answers.

It looks like impossible to get the time when it was fully loaded at least for iOS 11.1

I have found undocumented notification VKMapViewDidBecomeFullyDrawnNotification from VKMapView object but even it happens too early.

So it looks like only one way exist - wait couple seconds for full load and cross fingers to hope load the map was done.

Maxim Kholyavkin
  • 4,463
  • 2
  • 37
  • 82