How do you set initial size in iOS UIMapkit - ios

I want my mapView to display at the initial size (zoom level I determine) with phone location being centred in the map. Once this is done I want the user to be able to change zoom levels and pan to their hearts content. Next time they come into the app I want to reinitialise the map the same as the previous time.
Problem is when I come in it seems to set the map size before it has got a valid location fix.
Can anyone point me at an example that does best practice initialisation?

I solve this problem in next way. I think code is self-explained
- (void)mapView:(MKMapView *)mapView
didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (!_userLocated &&
userLocation.coordinate.latitude != 0.0 &&
userLocation.coordinate.longitude != 0.0)
{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span = MKCoordinateSpanMake(0.04, 0.04);
[mapView setRegion:mapRegion animated: YES];
_userLocated = YES;
}
}
And dont forget to set UIMapView delegate and Shows user location.

Related

iOS MapKit - How do I center the map on a specific location I get from text fields in a view?

iOS MapKit - How do I center the map on a specific location I get from text fields in a view?
It seems there are droves of tutorials on how to centre the map on the user's current location. How do I centre on a location I input manually?
Here's an example (in swift) I used recently, where I have a variable location - which you can create from your textfields, and you will also need to define height and width of the zoomed area
func centerMapOnLocation(location: CLLocation, regionRadius : CLLocationDistance)
{
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
latitudinalMeters, longitudinalMeters)
mapView.setRegion(coordinateRegion, animated: true)
}
In my example, the user has a configurable option to re-open the map at the last known position, or on the current location.
Here is an example in Objective-C on how to center your Map whenever the location of the user has been updated:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 1000, 1000);
[self.mapView setRegion:region animated:YES];
}
So, if you need to center your map/screen on a specific location, just copy the code inside this delegate method, and change the coordinate.

Tracking mode with MapKit

I have a question about UserLocation e MapKit.
I would to follow the user location on the map (automatically moves the map if user location changes) If the user tap (or pan or pinch) the map I would to disable the 'follow mode' (like Apple map app)..
I tried this method:
[_mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
This work well but I have some questions about it:
Is possible to set a particular zoom level during the 'follow mode'?
I noticed that if they are in 'follow mode' and I make a pinch the map, the 'follow mode' mode is not interrupted. If I still pinch the map (or pan) 'follow mode' is interrupted. I do not understand when you really stop this mode..
I think i know your means, you can try it, in MKMapViewDelegate:
func mapView(_mapView:MKMapView,didChangemode:MKUserTrackingMode,animated: Bool) {
mapView.setUserTrackingMode(.followWithHeading, animated: true)
}
When you scroll or zoom a mapView the MKUserTrackingMode will change,
so you can reset it.
You can set the region or the center of the map to the user location in its delegate methods:
Region:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 200.0f, 200.0f);
[self.mapView setRegion:region animated:YES];
}
Center:
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
Source: https://stackoverflow.com/a/19518422/3601482
I guess you want to reset the tracking mode back to auto follow:
[_mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
The question is when to reset. I would suggest
adding a button (like the navigation arrow button from Apple Maps), so the user can decide when to go back to auto tracking
or make a timer which resets on inactivity

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

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 .

how to show map scale during zoom of MKMapView like Apple's Maps.app

i am using MKMapView in my custom app and would like to show a map scale (tape measure) during zooming like Apple's Maps.app. Is this possible?
If not, and i would implement my own map scale, how can i get continious update information while the zoom ov the MKMapView is changed?
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
seems to be called only once at the begining of a zoom while
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
is called only once at the end of the zoom.
Maps.app map scale is shown and updated in realtime continiously during a zoom.
thanks in advance.
I had a similar problem, getting the camera.altitude based on user zooming, to display in a label.
Since there is no method such as "regionISChangingAnimated", but only WillChange and DidChange, I start a timer at WillChange and invalidate it at DidChange. The timer calls a method (updateElevationLabel) that calculates the altitude of the camera above the map.
However, since camera.altitude is not calculated until regionDidChange is called, use the zoomscale and the starting height of the map (zoomscale = 1.0 does not always equal altitude = 0m, it depends on where you are in the world) to calculate the current height. Starting height is a float in the method below, set it once on load and for each region change.
Finally you can change the format of the altitude, e.g. from km from m beyond a certain altitude (10'000 metres below).
For the oldschool: 1m = 3.2808399 ft.
-(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
if (showsElevation) {
//update starting height for the region
MKMapCamera *camera = map.camera;
CLLocationDistance altitude = camera.altitude;
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float factor = 1.0/currentZoomScale;
startingHeight = altitude/factor;
elevationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateElevationLabel)
userInfo:Nil
repeats:YES];
[elevationTimer fire];
}
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
[elevationTimer invalidate];
}
-(void)updateElevationLabel {
//1. create the label
if (!elevationLabel) {
elevationLabel = [UILabel new];
[elevationLabel setFrame:CGRectMake(0, 18, 200, 44)];
[elevationLabel setBackgroundColor:[UIColor redColor]];
[self addSubview:elevationLabel];
}
//2. grab the initial starting height (further updated on region changes)
if (startingHeight == 0) {
MKMapCamera *camera = map.camera;
CLLocationDistance altitude = camera.altitude;
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float factor = 1.0/currentZoomScale;
startingHeight = altitude/factor;
}
//3. get current zoom scale and altitude, format changes
MKZoomScale currentZoomScale = map.bounds.size.width / map.visibleMapRect.size.width;
float altitude = startingHeight * (1/currentZoomScale);
if (altitude>10000) {
altitude = altitude/1000;
[elevationLabel setText:[NSString stringWithFormat:#"%.1fkm", altitude]];
} else {
[elevationLabel setText:[NSString stringWithFormat:#"%.0fm", altitude]];
}
}
From Apple's docs on MKMapView's regionWillChangeAnimated: method (emphasis mine):
This method is called whenever the currently displayed map region
changes. During scrolling, this method may be called many times to
report updates to the map position. Therefore, your implementation of
this method should be as lightweight as possible to avoid affecting
scrolling performance.
Sounds like you should be able to use this method continuously as the map view scrolls, which solves part of your problem - so when that method is called, take a look at (I'm guessing here) the mapview's region attribute and derive your map scale from that.

Resources