MkmapView: How to keep the map centered while the user zoom - ios

I'm trying to implement the same map behaviour has Uber.
I would like to keep the map centered while the user zoom the map with a pinch or a double tap.
I tried implementing my own gesture recognizer but the result isn't great...
Does anyone got an idea of how to do it ?
Thanks !

For those interested in the solution i managed to do it.
First you need to add your gesture recognizer in the viewDidload
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(pinchOnMap:)];
[pinch setDelegate:self];
[pinch setDelaysTouchesBegan:YES];
[self.mapGestureContainer addGestureRecognizer:pinch];
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(zoomInGesture:)];
gestureRecognizer.delegate = self;
[gestureRecognizer setDelaysTouchesBegan:YES];
gestureRecognizer.numberOfTapsRequired = 2;
[self.mapGestureContainer addGestureRecognizer:gestureRecognizer];
Then implement the gesture function, it will handle the zoom based on the pinch scale. I added a small delay to avoid too much processing.
-(void) zoomInGesture:(UITapGestureRecognizer *) recognizer {
currentRegion = self.mapView.region;
currentSpan = self.mapView.region.span;
isZoomingWithDoubleTap = YES;
MKCoordinateRegion region = currentRegion;
MKCoordinateSpan span = currentSpan;
span.latitudeDelta = currentSpan.latitudeDelta / 2.3;
span.longitudeDelta = currentSpan.longitudeDelta / 2.3;
region.span = span;
[self.mapView setRegion:region animated:YES];
}
//Gesture used when the user pinch the area of the map
- (void) pinchOnMap:(UIPinchGestureRecognizer *) recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
isPinching = YES;
self.mapView.scrollEnabled = NO;
self.mapView.userInteractionEnabled = NO;
currentRegion = self.mapView.region;
currentSpan = self.mapView.region.span;
lastZoomTime = [[NSDate date] timeIntervalSince1970];
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
isPinching = NO;
self.mapView.scrollEnabled = YES;
self.mapView.userInteractionEnabled = YES;
}
if (recognizer.state == UIGestureRecognizerStateChanged) {
if (([[NSDate date] timeIntervalSince1970] * 1000) - lastZoomTime >= 20) {
lastZoomTime = [[NSDate date] timeIntervalSince1970] * 1000;
MKCoordinateRegion region = currentRegion;
MKCoordinateSpan span = currentSpan;
span.latitudeDelta = currentSpan.latitudeDelta / recognizer.scale;
span.longitudeDelta = currentSpan.longitudeDelta / recognizer.scale;
region.span = span;
[self.mapView setRegion:region animated:NO];
}
}
}

In your map view delegate, do this
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}

Related

MKMapView won't respond to touching or pinching

So I have a MKMapVie object and it is set up as follows:
self.mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0,
[Screen width],
[Screen height])];
self.mapView.mapType = MKMapTypeSatellite;
self.mapView.userInteractionEnabled = YES;
self.mapView.scrollEnabled = YES;
self.mapView.showsUserLocation = YES;
[self addSubview:mapView];
And I set its location to be the current user location with the following method.
- (void) locateUser {
float z2 = 0.003 * 4.0;
MKCoordinateSpan span = MKCoordinateSpanMake(z2, z2);
[self.mapView setRegion: MKCoordinateRegionMake(currentLocation, span) animated:YES];
}
However once the map zooms to the users location it will not allow me to move the map around or pinch or zoom. Is there something i'm missing?
Thanks.

How to Set a Region for a MKMapView?

I am new in iOS apps development and I want to put a Map in My application with a pushpin on an exact location using its latitude and longitude. I add the Map but the problem is it always appear with its initial position an the push pin doesn't appear (It is on the map but you need to change the position to see it ) . Like this figures shows :
Initial Position
2.- After I moved the map's position
What I want is to show the position of the pushpin as a default location and with a zoom .
This is a sample of My Code :
- (void)viewDidLoad
{
[super viewDidLoad];
CLLocationCoordinate2D myCoordinate = {20.5, -7.06}; //AS an Example
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = myCoordinate;
//Drop pin on map
[self.mapView addAnnotation:point];
//Region with Zoom
-(void)viewWillAppear:(BOOL)animated {
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 20.5;
zoomLocation.longitude= -7.6;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
[self.mapView setRegion:viewRegion animated:YES];
}
I used this code but still the same thing it always display the initial position (Figure 1)
Thank You .
(void)viewDidLoad
{
[super viewDidLoad];
MapAnnotation *ann = [[MapAnnotation alloc] init];
MKCoordinateRegion region;
region.center.latitude = 31.504679;
region.center.longitude = 74.247429;
region.span.latitudeDelta = 0.01;
region.span.longitudeDelta = 0.01;
[mapView setRegion:region animated:YES];
ann.title = #"Digital Net";
ann.subtitle = #"Office";
ann.coordinate = region.center;
[mapView addAnnotation:ann];
}
(MKAnnotationView *) mapView:(MKMapView *)mpView viewForAnnotation:(id)annotation {
MKPinAnnotationView *pinView = nil;
if(annotation != mapView.userLocation) {
static NSString *defaultID = #"myLocation";
pinView = (MKPinAnnotationView *)[mpView dequeueReusableAnnotationViewWithIdentifier:defaultID];
if(pinView == nil) {
pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:defaultID];
pinView.pinColor = MKPinAnnotationColorGreen;
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
}
}
return pinView;
}
I have done something like that and it was working fine

Plot pin does not point exact place on MKMapView

- (void)viewDidLoad
{
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(foundTap:)];
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
[self.myMapView addGestureRecognizer:tapRecognizer];
}
-(void)foundTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self.myMapView];
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point toCoordinateFromView:self.view];
MKPointAnnotation *point1 = [[MKPointAnnotation alloc] init];
point1.coordinate = tapPoint;
[self.myMapView addAnnotation:point1];
}
I used above code to make pin point on MKMapView when i choose a place in MKMapView it does not point where i exactly touches.It goes little far where i touches.What is wrong with my code?.any help will be appreciated.thanks in advance.
Try changing the passed-in object to the locationInView: method to self.view:
-(void)foundTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self.view];
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point toCoordinateFromView:self.view];
MKPointAnnotation *point1 = [[MKPointAnnotation alloc] init];
point1.coordinate = tapPoint;
[self.myMapView addAnnotation:point1];
}
I know the other answer is working for you, but I just wanted to show the proper way to use convertPoint:toCoordinateFromView:. Instead of passing it the container view, it should be passed the map view that the point is in:
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point
toCoordinateFromView:self.myMapView];
That saves swapping between views. Here's the full method:
-(void)foundTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self.myMapView];
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point toCoordinateFromView:self.myMapView];
MKPointAnnotation *point1 = [[MKPointAnnotation alloc] init];
point1.coordinate = tapPoint;
[self.myMapView addAnnotation:point1];
}

how can I limit the map area to only one country in iOS?

I am making an app for iOS using mapkit. I want to limit the boundaries of the map only to a specific region/country. Is there a way to do this?
There's no way to tell the map not to scroll out of a certain area. The only way I could think to do it would be to stop the user from scrolling when you hit one of your fences. The example below is written without testing or compiling at all so you may need to tweek it yourself but hopefully it'll get you started..
ViewController.h
CLLocationCoordinate2D myNorthEast, mySouthWest;
ViewController.m
-(void)viewDidLoad{
myNorthEast = CLLocationCoordinate2DMake(lat1,lon1);
mySouthWest = CLLocationCoordinate2DMake(lat2,lon2);
[super viewDidLoad];
}
-(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{
/*
/ Check if the map region is going to change outside your fence.
/ If so, programmatically set it back to the edge of your fence.
*/
if(!animated){
return; // Don't want to get stuck in a loop after you set your region below.
}
MKCoordinateRegion region = [mapView region];
// You will need to get the NE and SW points of the new region to compare
// First we need to calculate the corners of the map so we get the points
CGPoint nePoint = CGPointMake(mapView.bounds.origin.x + mapView.bounds.size.width, mapView.bounds.origin.y);
CGPoint swPoint = CGPointMake(mapView.bounds.origin.x, bounds.origin.y + mapView.bounds.size.height);
// Then transform those point into lat,lng values
CLLocationCoordinate2D neCoord;
neCoord = [mapView convertPoint:nePoint toCoordinateFromView:mapView];
CLLocationCoordinate2D swCoord;
swCoord = [mapView convertPoint:swPoint toCoordinateFromView:mapView];
/*
You will need to mess around with the lat/lon & sign of the new center calculation for the other cases.
*/
if(neCoord.latitude > myNorthEast.latitude){
MKCoordinateRegion newRegion;
newRegion.span = region.span;
CLLocationCoordinate2D newCenter;
newCenter.longitude = region.center.longitude;
newCenter.latitude = myNorthEast.latitude - region.span.latitudeDelta;
newRegion.center = newCenter;
[mapView setRegion:newRegion animated:NO];
}else if(neCoord.longitude < myNorthEast.longitude){
}else if(swCoord.latitude < mySouthWest.latitude){
}else if(swCoord.longitude > mySouthWest.longitude){
}
}
Part of this comes from this answer:
Getting the bounds of an MKMapvIew
Here is a further specification of the previous answer. Note that this will be different for different parts of the world:
if(neCoord.latitude > myNorthEast.latitude){
MKCoordinateRegion newRegion;
newRegion.span = region.span;
CLLocationCoordinate2D newCenter;
newCenter.longitude = region.center.longitude;
newCenter.latitude = myNorthEast.latitude - region.span.latitudeDelta / 2;
newRegion.center = newCenter;
[mapView setRegion:newRegion animated:NO];
} else if(swCoord.latitude < mySouthWest.latitude){
MKCoordinateRegion newRegion;
newRegion.span = region.span;
CLLocationCoordinate2D newCenter;
newCenter.longitude = region.center.longitude;
newCenter.latitude = mySouthWest.latitude + region.span.latitudeDelta / 2;
newRegion.center = newCenter;
[mapView setRegion:newRegion animated:NO];
} else if(neCoord.longitude > myNorthEast.longitude){
MKCoordinateRegion newRegion;
newRegion.span = region.span;
CLLocationCoordinate2D newCenter;
newCenter.longitude = myNorthEast.longitude - region.span.longitudeDelta / 2;
newCenter.latitude = region.center.latitude;
newRegion.center = newCenter;
[mapView setRegion:newRegion animated:NO];
} else if(swCoord.longitude < mySouthWest.longitude){
MKCoordinateRegion newRegion;
newRegion.span = region.span;
CLLocationCoordinate2D newCenter;
newCenter.longitude = mySouthWest.longitude + region.span.longitudeDelta / 2;
newCenter.latitude = region.center.latitude;
newRegion.center = newCenter;
[mapView setRegion:newRegion animated:NO];
}

MKCircle is not updating radius but it's translating

I've to draw an MKCicle into an MKMapView. Then I've to re-draw it when user, through a slider, change the radius. I remove it and I re-create it, re-adding it to the map.
But instead of do what I'm expecting, I see the MKCircle translating over the map, maintaining the same size.
Here's my code:
- (MKOverlayView *)mapView:(MKMapView *)map viewForOverlay:(id)overlay
{
MKOverlayView* overlayView = nil;
if(overlay == self.circle)
{
//if we have not yet created an overlay view for this overlay, create it now.
if(nil == self.circleView)
{
self.circleView = [[[MKCircleView alloc] initWithCircle:self.circle] autorelease];
self.circleView.fillColor = [UIColor blueColor];
self.circleView.strokeColor = [UIColor blueColor];
self.circleView.alpha = 50;
self.circleView.lineWidth = 2;
}
overlayView = self.circleView;
}
return overlayView;
}
-(void)drawPolygonWithLocation
{
[self.mapView removeOverlay: self.circle];
MKCoordinateRegion region;
region.center.latitude = self.geofenceLocation.latitude;
region.center.longitude = self.geofenceLocation.longitude;
region.span.latitudeDelta = 0.005;
region.span.longitudeDelta = 0.005;
MKCoordinateRegion adjustedRegion = [self.mapView regionThatFits: region];
[self.mapView setRegion:adjustedRegion animated:TRUE];
self.radius = (double)(slRadius.value);
NSLog(#"Raggio: %f", self.radius);
NSLog(#"Lat: %f, Lon: %f", region.center.latitude, region.center.longitude);
self.circle = [MKCircle circleWithCenterCoordinate:self.geofenceLocation.coordinate radius: self.radius];
NSLog(#"CIRCLE: radius %f Lat: %f, Lon: %f", self.circle.radius, self.circle.coordinate.latitude, self.circle.coordinate.longitude);
[self.mapView addOverlay:self.circle];
}
-(IBAction)updateRadius:(id)sender
{
[self drawPolygonWithLocation];
}
The NSLog is writing into the console right values, the center doesn't change and the radius changes according to the user input.
But, again, the MKCircle translates going on the north-west.
Thanks in advance,
Samuel Rabini
Fixed.
I just add
self.circleView = nil;
before the
[self.mapView addOverlay:self.circle];
in this way it works fine.
Samuel

Resources