iOS mapview - zoom to pin and then activate callout - ios

I have a mapview in xcode, which is all working well.
What my page does just now is like this:
downloads a bunch of data and locations from a backend database
populates a mapview with locations and drops pins
populates a table underneath the mapview
That all works great, and I end up with a mapview with a load of pins, and a tableview that has the details of those pins.
What I want to do now, is allow the user to tap on a row from the tableview, and have the map zoom and centre to the corresponding map pin, and then automatically activate the annotation pin callout.
In my 'didselectrow' method, I have the following:
MKCoordinateSpan span = MKCoordinateSpanMake(0.1f, 0.1f);
CLLocationCoordinate2D coordinate = { [item.latitude floatValue], [item.longitude floatValue] };
MKCoordinateRegion region = { coordinate, span };
[self.mapView setRegion:region animated:YES];
This works great too. Tapping on the table row will zoom to and centre the map pin at this location.
I just can't get the last step of firing the annotation pin callout to work.
I have tried:
[mapview annotationsInMapRect:mapview.visibleMapRect];
But this isn't working, and it is possible that there still might be 2 or 3 map pins in the visible area.
What I need to do is to get the pin nearest to the centred location (see above - item.latitude / item.longitude) to automatically open it's callout.
Everything in the code is set up and working, and the map pins have callouts that fire when tapped on, I just need this last stage of having the pin nearest the centre location to open automatically.
Can anyone help with this?
I have tried various other suggestions on SO, but none seem to fit this requirement.

I think I have got solution for your problem you need to use this [_mapView setSelectedAnnotations:#[[[self.mapView annotations] lastObject]]];
For testing I have created an small project that have these 2 methods.
- (IBAction)buttonTouched:(id)sender {
[_mapView showAnnotations:[self.mapView annotations] animated:YES];
[self performSelector:#selector(showAnnotationCallOut) withObject:nil afterDelay:1.0f];
}
- (void) showAnnotationCallOut {
[_mapView setSelectedAnnotations:#[[[self.mapView annotations] lastObject]]];
}
Note: I have called just one annotation for test that why I am calling last object. You'll need to call it for specific annotation of your annotation array.
Edit: According to Richerd's comment here is solution for problem of finding the annotion and showing the callout fro that.
for (MapViewAnnotation *annotion in [self.mapView annotion]) {
if ([annotion.identifire isEqualToString:annotationToCallCallOutIdentifier]) {
//[_mapView setSelectedAnnotations:#[annotation]];
[_mapView selectAnnotation:annotation animated:YES];
break;//don't break if there are can be more than one callouts
}
}

Related

How do I specify the zoom level when using an MKUserTrackingBarButtonItem?

I am using a MKUserTrackingBarButtonItem button to allow the user to automatically track their location on a map. The problem is that when they tap this button, it is zoomed too far out. I want it to start at a specified zoom level (i.e. span). How can I achieve this?
When the user taps the button to change to MKUserTrackingModeFollow, it seems to use the same zoom level that the user last manually changed to (i.e. using gestures on the map). Attempting to specify a different zoom level via setRegion or setVisibleMapRect does not affect what zoom level will be used when the mode is changed to MKUserTrackingModeFollow.
Attempting to override mapView:didChangeUserTrackingMode: to set the region causes the mode to be changed back to MKUserTrackingModeNone. Example:
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
If I attempt to reset the mode immediately after setting the region, it works fine if the user is stationary, but zooms back out if the user is moving.
The simplest solution would be if there was a way to simply specify something like a zoom level for MKUserTraking by sending it my span value. However, since that doesn't seem to exist, what else can I do?
I had the same issue and used a different approach to fix it. You can use the MapCamera function for this instead of that button.
On each new location do this:
MKMapCamera *newCamera = [MKMapCamera cameraLookingAtCenterCoordinate:[newLocation coordinate]
fromEyeCoordinate:[oldLocation coordinate]
eyeAltitude:2000];
[mapView setCamera:newCamera animated:TRUE];
And play with the eyeAltitude.
If the user manually zooms in or out you can read the altitude value from mapview.camera.altitude also don't update the camera when the user is manually using the map.
According to apple documentation used here
https://developer.apple.com/reference/mapkit/mkmapview/1616208-usertrackingmode
Setting the tracking mode to follow or follow​With​Heading causes the map view to center the map on that location and begin tracking the user’s location. If the map is zoomed out, the map view automatically zooms in on the user’s location, effectively changing the current visible region.
Here changing the region does not effect your visible region due to that reason.
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
So you just need to change center coordinate on didChangeUserTrackingMode instead of changing the whole region
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
[self.mapView setCenterCoordinate:mapView.userLocation.location.coordinate animated:YES];
}
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:mapViewuserLocation.location.coordinate animated:YES];
}
on click of MKUserTrackingBarButtonItem change the zoom level
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];

Automatic annotation callouts after some zoom level

I would like to display annotation callouts automatically after a specific zoom level. However, I do not want to have all the annotation callouts, but only those which are being shown in the screen.
To display annotation callout of those that are visible on screen just the code below should do the trick, as it relies on the selectAnnotation method of MKMapView, of course, after detecting the desired zoom level:
for (MKAnnotation *annotation in mapView.annotations) {
if ( MKMapRectContainsPoint(mapView.visibleMapRect, MKMapPointForCoordinate(annotation.coordinate)) ) {
[mapView selectAnnotation:annotation animated:YES];
}
}

MKMapView doesn't zoom correctly while user tracking mode is MKUserTrackingModeFollowWithHeading

I created a test project with few lines of code and with two components: MKMapView and UIButton. I ticked mapView option - Shows user location. Also I defined an action for the button, it zooms the map to user location.
Here is code from controller:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
self.mapView.delegate = self;
}
- (IBAction)changeRegion:(id)sender {
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.mapView.userLocation.coordinate, 200.0f, 200.0f);
[self.mapView setRegion:region animated:YES];
}
Pretty simple and straightforward, isn't it? But when I tap the button I see weird behaviour: map view zooms to specified region then returns back to original zoom. What's the problem? How can I keep zooming and track user location at the same time?
I notice similar behaviour with MKUserTrackingModeFollow tracking mode.
P.S. I forgot to mention that it's a problem mostly for iOS7
From apple documentation:
Setting the tracking mode to MKUserTrackingModeFollow or
MKUserTrackingModeFollowWithHeading causes the map view to center the
map on that location and begin tracking the user’s location. If the
map is zoomed out, the map view automatically zooms in on the user’s
location, effectively changing the current visible region.
If you want both to adjust the region and to track the user, I suggest you check for location updates and adjust zoom accordingly.
For example:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 200.0f, 200.0f);
[self.mapView setRegion:region animated:YES];
}
EDIT
Instead of setting the region, try just setting the center,
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
and let your button action set the zoom, keeping the same center:
- (IBAction)changeRegion:(id)sender {
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.mapView.centerCoordinate, 200.0f, 200.0f);
[self.mapView setRegion:region animated:YES];
}
And very important: do not set your mapView to track user. Disable tracking user because now you are tracking it yourself. I think the default is MKUserTrackingModeNone .

Stop MKMapView from moving to user's current location when user moves the map

I am making an app to show some places near the user's current location. When the user moves the map, the map moves the user to his current location, over and over again. So, the user can't see the annotations on the map, because if he moves the map, the map goes back to his current location.
This is what I am doing right now:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (userLocation)
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 1000, 1000);
MKCoordinateRegion regionDelMapa = [self.mapa regionThatFits:region];
if(isnan(regionDelMapa.center.latitude))
{
NSLog(#"Bad region...");
}else
{
[self.mapa setRegion:regionDelMapa animated:YES];
}
}
}
I have tried some other answers here, but nothing happens. This is not the desired behavior, so how can I stop updating the user's current location?
[self.mapa setRegion:regionDelMapa animated:YES];
That line sets the map region. You're executing that code every time you get an update, thus reseting the area that the map covers. If you don't want the map to re-center itself on the user's location at each update, remove that code from the -mapView:didUpdateUserLocation: method.
If you want to center the map on the user once and then allow the user to scroll away from that location, use Core Location to get the user's location.
Please use this API : [locationManager stopUpdatingLocation];
Add this in your function didUpdateUserLocation .

How to store current MKCoordinateRegion or zoom level?

I have an app which is providing a zoomable MKMapView to the user. I want to be able to store the user's preferred coordinates and zoom level for when the map view is first displayed.
Currently, in viewDidLoad, I am providing a default set of coordinates and zoom level for the initial map presentation:
zoomLocation.latitude = 55.50;
zoomLocation.longitude = -5.50;
// specify size of region to display
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 340.0*METERS_PER_MILE, 340.0*METERS_PER_MILE);
// auto adjust region to fit screen
MKCoordinateRegion adjustedRegion = [mapView regionThatFits:viewRegion];
// display the new region
[mapView setRegion:adjustedRegion animated:YES];
What I am trying to do is, when the user scrolls and zooms the map to their preferred default view, they can hit the "Set Default" button and store the required properties to implemented in future whenever the view loads.
To store the coordinates of the user's selected view, I have this:
// gets coordinates of currently viewed map image
CGPoint pointCentrePoint = CGPointMake(mapView.frame.size.width/2, mapView.frame.size.height/2);
centrePoint = [mapView convertPoint:pointCentrePoint toCoordinateFromView:mapView];
NSLog(#"LAT: %f LON: %f", centrePoint.latitude, centrePoint.longitude);
What I'm struggling with is how to store the user's selected MKCoordinateRegion, or zoom level. Is there a way I can access this property for the current view, so it can be reused in future when the view loads?
Unlike the JavaScript Google Maps api, the MKMapView doesn't have a readily available "zoom level" property (it isn't really necessary for your purpose).
Work with the center and span values in the region property.
This question gives an example of how to save/load the region to NSUserDefaults.

Resources