How to tell when MKMapView and visible MKAnnotationView are finished drawing? - iphone

I'm displaying a MKMapView with MKAnnotations some of which are selected and showing their Annotation.
I am trying to grab an image of the displayed map and annotations using the -renderInContext.
90% of the time the Map, MKPinAnnotationView's and selected annotations are correctly captured.
The other 10% of the time the image is missing something, usually the MKPinAnnotationViews or their annotations if selected.
I've added code to deal with the Map itself loading it's map data.
But I haven't been able to track down something that would indicate to me that all of the visible MKPinAnnotationView's have been drawn
and if selected their annotations displayed??
the closest hint I've come across is the addObserver, although I haven't figured out what could be observed that would tell me when all of the drawing is done.
Thoughts?

ok I'm an idiot...
I finally tracked down the problem. In my viewForAnnotation routine in the MKMapView Delegate protocol I wasn't correctly setting values for reused MKPinAnnotationView's.
So some of the time I would reuse a view that had .canShowCallout set to YES and other times I'd reuse a view that had .canShowCallout set to NO.
.<

Try using the MKMapViewDelegate didAddAnnotationViews method.
If in that method, the drawing is still not ready for your requirements, you could then in there call your capturing method with performSelector:withObject:afterDelay:.

Related

How to show UIProgressView with MKMapView?

In my application, i have latitude and longitude values to show a location in MKMapView. In previous version i have used UIActivityIndicator until Mapscreen loads in MKMapView. Now i want to show a UIProgressView instead of UIActivityIndicator.
Is it possible to show a progress with MKMapView? I searched lot and couldn't get proper or related solution so for.
Note:
What i have used with UIProgressView is uploading and downloading files with NSURLConnection. I want to know the process time of map loading section, then only i will able to show progress ? right.
Any one plz help me on this? Thanks in advance.
Unfortunately MKMapView doesn't natively support this.
You can check MKMapViewDelegate Protocol Reference and there are no such delegate methods. All you can get from it is only when map starts and finishes loading the data.

Why is the MKMapView's userLocation property rubbish ... for a while?

I have a Map View defined in IB and it is set to show the user location.
In my app, in -viewDidAppear, I query self.mapView.userLocation.location.coordinate and it comes back with insane values such as:
latitude: 4.8194501961644877e-49
longitude: 2.2993313035571993e-59
However, the next time -viewDidAppear is called (after I've simply moved to another tabbed view and then back to this one) the userLocation property holds exactly the correct values for my current location.
It seems that at the time of my initial call, the userLocation property has not been initialised but despite having read Apple's documentation I can't see any caveats where it says that this property is only valid after doing xxx.
Is there something that has to happen before userLocation is valid to use or should I just use CLLocationManager and ask it instead?
Thanks in advance for any help.
Sadly, Thomas' suggestion didn't help. What I have since discovered is:
If showsUserLocation is NO, then userLocation is never set correctly and -MapView:didUpdateUserLocation: is never called, consequently I never ever get a sensible location value.
So, to get the user's location I have to set showsUserLocation to YES, however that then means that after all my annotations have been added to the view (without including the user's location) I then calculate the required span to encompass them all and display them all at the right zoom level. After I do that though, the view jumps sideways as the Map View then automatically displays the user's location as the blue blob! As it was never included in the annotations to work out the zoom level I can't incorporate it into my calculations. Aaargh!
Note that when showsUserLocation is YES, then -MapView:didUpdateUserLocation: is called, but only after I've calculated all the coordinates of my annotations, not before!
I'm assuming it hasn't finished finding the user location - it has to work this out and it may take a while.
Instead of using it in viewDidLoad use THIS delegate method:
- (void)mapView:(MKMapView *)myMapView didUpdateUserLocation:(MKUserLocation *)userLocation;
You will need to set your mapview delegate to self. :)
Same is often true of Core Location. You'll get the last location lingering it its buffer, sometimes, or a super-broad throw-the-dart-at-the-map kind of location...
Best bet is to check the .horizontalAccuracy property of the location object and toss any that are too vague. It's good practice to just chuck the first one too.
for didUpdateUserLocation to be called you have to have...
mapView.showsUserLocation = TRUE;

moving/updating MKOverlay on MKMapView

is there a way to update (i.e. moving around) a MKOverlay that is already added to the MKMapView. Removing a old one and adding a new one is terrible (slow).
i.e i would like to trigger the background function that is calling this function when an overlay moves on the screen:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
(with MKAnnotions its a little better i think, but i cant use MKPolyline, MKPolygon, etc. and the whole information is reduced to a single point)
MKOverlayView has the following methods which force MapKit to re-render the given mapRect:
- (void)setNeedsDisplayInMapRect:(MKMapRect)mapRect
- (void)setNeedsDisplayInMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale
If you’re using a timer (or periodical HTTP request or some sort of other method for determining that your overlay should be updated), calling one of the above methods on the overlayView will cause it to re-render that spot on the map (i.e. -canDrawMapRect:zoomScale: will be called again, and then -drawMapRect:zoomScale:inContext: will be called if the former returns YES).
Update:
If you’re not sure what mapRect you need to re-render, you might be able to use the MKMapRectWorld constant as the mapRect — which I believe would cause the overlay across the entire map to reload (once visible).
Use MKAnnotations. You can change the coordinate of them. Just disable any touch-related stuff. You would need your own drawing code to draw the annotations, OpenGL would probably do the trick. Nobody would know the difference.
I actually had to force the overlay view to invalidate the path, to clear out the old path before setting up a new one. [polygonView invalidatePath]. Telling the map view that it needs a display refresh was insufficient for me.
This seems to force the map to refresh overlays. Probably annotations as well.
[self.mapView setCenterCoordinate:self.mapView.centerCoordinate];
Swift 3+ update, this worked for me, thanks #zenchemical for the inspiration.
DispatchQueue.main.async {
self.map.add(overlay, level: .aboveRoads)
self.map.setCenter(self.map.centerCoordinate, animated: false)
}
Side note, I can't help feeling that this shouldn't be necessary and there is a more appropriate solution, however, I haven't found it. Even though the add command is dispatched to the main queue, the overlay reproducibly does not render until I move the map.
there is a problem with raw setNeedsDisplay if the bounding rectangle of the polygon changes then MKMapView would not update whole rectangle correctly,and secondly,MKPolygon/MKPolygonRenderer's polygon is not updatable,and the problem with add/remove pipeline is if there is too much request to update the MapView it can get slower in the pipeline, I currently use a 30Hz thread loop if there is an update in polygon i do add/remove

MapKit custom annotations being added to map, but are not visible on the map

I have an interface with a mapView and UITableView. Data is loaded from a server, and the annotations are created and added to the map with
[mapView addAnnotation:truck]
the tableview is then populated using the array thats retured from
[mapView annotations]
once this process is completed, i check the number of annotations on the map with [[mapView annotations] count] called whenever i click on a cell in the table and its equal to the number it ought to be, so all the annotations are getting added onto the mapView, but for some reason I cant see any annotations in the simulator.
The images are named just as they are assigned in the custom AnnotationView, the loadAnnotation function is done properly, etc... i dont know what it could be but ive looked at the associate between the image file and wheres its loaded a hundred times to find a discrepancy, but it all looks fine.
One interesting point is that when i print and coordinate value after clicking on the cell (remember this data comes straight from [mapView annotations], it looks good... but for whatever reason the annotation view isnt being displayed.
so i suppose if i could have the answer to one question it would be, what are possible causes for a mapView to contain several annotations, but to not show any on the map?
Thanks
EDITED WITH IMPORTANT ADDITIONAL INFO
There appears to be a disconnect between what is being displayed on my map in the simulator and what im seeing in the mapView object. For example, when i select a row in the tableView, I am calling:
[mapView setRegion:MKCoordinateRegionMake([annotation coordinate], MKCoordinateSpanMake(.01, .01)) animated:YES];
and this has no effect on the map in the simulator. I have checked that the coordinate being passed is actually a valid coordinate, but it doesnt seem to matter because the map is being unresponsive. I thought it could be something wrong with my .xib, but its all connected properly. delegate is the outlet and mapView as a referencing outlet.
Does anyone now understand what may be happening?
This really looks like missing images.
Try replacing your custom annotations with MKPinAnnotations to test, and see if your annotations are visible then.
Have you implemented the following delegate function ?
(MKAnnotationView*) mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation;
This line:
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
was creating messing everything up. once i commented it out, the app began working fine.

How can we pause adding annotations while MKMapView is animating or moving. [iPhone SDK 3]

I have a MKMapView and another class has a thread adding annotations to the MKMapView.
But the problem is, MKMapView.annotations is nonatomic. So while the other class is adding annotations.... It looks OK if user do not move the map's visible area. But if you keep moving it, and the other class was adding annotations to the map at that moment, app crashes.
Because the array was mutated during map was animating.
I made NSOperationQueue, and add each adding annotation job to the queue.
And then whenever map's region is changed, pause the NSOperationQueue.. and then restart it when region change is done.
I could get rid of most crashes.. but still it is happened occasionally.
Any idea?
Don't add the annotations to the MKMapView in a background thread. Let the background operation return an NSArray back to the main thread, then add the NSArray to the MKMapView synchronously. I do this all the time and it works.
Always handle UI controls on the main thread.