Using IBAction Buttons to Zoom MapView
Asked Answered
F

6

8

I have an issue. My current location is displayed and centered in a map view however the map region doesn't get zoomed in to. I tried taking Rob's advice by taking span and region out of the didUpdateToLocation method but I must not have implemented it right. I don't think it's recognizing my call to setRegion in viewDidLoad and my buttons aren't being recognized. Please check my code below and point out the mistake(s). My goal is to be able to zoom in and out of my location using the IBAction buttons.

.h

- (IBAction)zoomIn:(id)sender;

- (IBAction)zoomOut:(id)sender;

.m in viewDidLoad

double miles = 0.5;

MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/69.0;

MKCoordinateRegion region;
region.span = span;

[self.mapView setRegion:region animated:YES];

[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];

_mapView.mapType = MKMapTypeSatellite;

.m in my didUpdateToLocation method.

[self.mapView setCenterCoordinate:newLocation.coordinate animated:YES];

.Zoom In:

- (IBAction)zoomIn:(id)sender 
{
    MKCoordinateSpan span;
    span.latitudeDelta = _mapView.region.span.latitudeDelta * 2;
    span.longitudeDelta = _mapView.region.span.latitudeDelta * 2;
    MKCoordinateRegion region;
    region.span = span;
    region.center = _mapView.region.center;

    [self.mapView setRegion:region animated:YES];
}

.Zoom Out :

- (IBAction)zoomOut:(id)sender
{
     MKCoordinateSpan span;
     span.latitudeDelta = _mapView.region.span.latitudeDelta / 2;
     span.longitudeDelta = _mapView.region.span.latitudeDelta / 2;
     MKCoordinateRegion region;
     region.span = span;
     region.center = _mapView.region.center;

     [self.mapView setRegion:region animated:YES];
}
Footer answered 2/3, 2013 at 17:34 Comment(0)
T
12

You can get the current region, multiply or divide the span by two, as appropriate (dividing on zoom in, multiplying on zoom out), and then set the region:

- (IBAction)zoomIn:(id)sender {
    MKCoordinateRegion region = self.mapView.region;
    region.span.latitudeDelta /= 2.0;
    region.span.longitudeDelta /= 2.0;
    [self.mapView setRegion:region animated:YES];
}

- (IBAction)zoomOut:(id)sender {
    MKCoordinateRegion region = self.mapView.region;
    region.span.latitudeDelta  = MIN(region.span.latitudeDelta  * 2.0, 180.0);
    region.span.longitudeDelta = MIN(region.span.longitudeDelta * 2.0, 180.0);
    [self.mapView setRegion:region animated:YES];
}

If you want to have the map automatically zoom to your location, you can use:

self.mapView.userTrackingMode = MKUserTrackingModeFollow;

If you want to do this manually, you can do something like the following. First, define an class property for whether you've zoomed in already or not:

@property (nonatomic) BOOL alreadySetZoomScale;

then change your didUpdateLocations (or didUpdateToLocation) to check this and set the zoom scale once:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation* newLocation = [locations lastObject]; // if less than zero, then valid lat and long not found

    if (newLocation.horizontalAccuracy < 0)
        return;

    if (!self.alreadySetZoomScale)
    {
        MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 1609, 1609); // 1 mile = 1609.34 meters
        self.mapView.region = region;
        [self.mapView setRegion:region animated:YES];
        self.alreadySetZoomScale = YES;
    }
    else
    {
        [self.mapView setCenterCoordinate:newLocation.coordinate animated:YES];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    // if prior to iOS 6, use this old `MKMapViewDelegate` method, but call our
    // other routine.

    if (SYSTEM_VERSION_LESS_THAN(@"6.0"))
        [self locationManager:manager didUpdateLocations:@[newLocation]];
}

This basically says, "if I haven't zoomed in yet, set the region based upon the newLocation and a certain number of meters around that location, but if I have already done that, the just set the center coordinate based upon my current location, but don't change the zoom scale (in case I already zoomed in or out). This also does some conditional iOS version number logic (using the macros shown here), making sure if the end-user is running iOS prior to 6.0, that it will call our updated method.

By the way, if you're showing the user location on the map (e.g. self.mapView.showsUserLocation = YES;), you might want to have this didUpdateLocations also remove the overlay associated with the MKUserLocation before moving the map center, otherwise it can leave the old overlay sitting around:

[self.mapView removeOverlays:self.mapView.overlays];
Tendentious answered 2/3, 2013 at 19:28 Comment(10)
Same here. Please read my comment to Manu. Thanks for the response.Footer
@user1886166 You're setting the span in didUpdateToLocation. Don't. Do it once (e.g. the first time you get the location) and after that point, just use the MKMapView method, setCenterCoordinate, but don't mess around with it after that point. Frankly, if you want your mapview to follow the user as they move, you probably shouldn't be doing this manually, anyway. You should consider setting userTrackingMode property of MKMapView.Tendentious
I gave it a try but it's still not right. Please check and tell me what I'm doing wrong. Sorry, I'm not very familiar with MKMapView but I do understand what you're saying.Footer
@user1886166 Two things: 1. You probably want to call MKCoordinateRegionMakeWithDistance once, just so you zoom into the correct initial zoom level. 2. On your zooming buttons, I think you have your multiple and divide backwards (like Manu did). Anyway, see my revised answer which shows the idea behind setting region the first location update, but then using centerCoordinate after that.Tendentious
This almost works! The problem is that it only sets the region once when in actuality it needs to do it every time the view loads. My app, using a navigation controller, opens to a menu with buttons for different tools. Choosing the button for Locationing opens up the mapView and displays the user location with the functioning zoom buttons (perfect!). The problem is that when you press back to the main menu and reenter Locationing, the map doesn't zoom back to region but only sets user location to center since it's already set region once earlier. My zooms won't function at this point either.Footer
At this point, we've solved one problem but created another. Perhaps put something in viewDidLoad or something in your if statement about the viewDidLoad. I don't want to be a burden but I'd really appreciate it if you can help me come up with a solution to this.Footer
@user1886166 My bad. Use a class property rather than a static. See revised answer. Statics are remembered across instances, and we really want this BOOL variable, alreadySetZoomScale to be reset every time. Or you could reset the static in viewDidLoad. But the best solution is to make it a class property or ivar, like I did above.Tendentious
@user1886166 In terms of your zooms not working, it's hard for me to say (because the above code works fine for me). Perhaps your Interface Builder linkage to the IBOutlet references is not hooked up properly. Try putting a log or breakpoint in those methods and see if they're getting called at all. Alternatively, if you compress your project and upload it somewhere, I'm happy to take a quick look at it.Tendentious
I made some minor changes to make it work for me but that did it - THANK YOU! I will go ahead and show you how it ended up in my answer. On a side note, I'm not sure what you were talking about when you mentioned moving the annotation before the map center. I have some annotations on my map but what annotation are you talking about? And I'm not sure what you mean by leaving the old overlay sitting around. None the less, I'm very pleased with the help you provided. I'll comment if anything pops up later but I think you nailed it.Footer
@user1886166 My comment about "removing the annotation" (and I meant "removing the overlay") was unclear and was discussing a minor detail: What I've noticed is that if you have your MKMapView set up to show the user's current location, that if it hasn't locked in on the user's location yet, iOS adds a circular overlay, to show you how far off the GPS location might be. I've noticed that if you recenter a map while that's showing, that overlay will never get removed. I've tried to clarify my answer further. In answer to your question, don't worry about your annotations, though.Tendentious
B
1

Try this. But I haven't tested yet.

- (IBAction)zoomIn:(id)sender {
      MKCoordinateSpan span;
      span.latitudeDelta = _mapView.region.span.latitudeDelta * 2;
      span.longitudeDelta = _mapView.region.span.latitudeDelta * 2;
      MKCoordinateRegion region;
      region.span = span;
      region.center = _mapView.region.center;

     [self.mapView setRegion:region animated:YES];
 }

 - (IBAction)zoomOut:(id)sender {
      MKCoordinateSpan span;
      span.latitudeDelta = _mapView.region.span.latitudeDelta / 2;
      span.longitudeDelta = _mapView.region.span.latitudeDelta / 2;
      MKCoordinateRegion region;
      region.span = span;
      region.center = _mapView.region.center;

     [self.mapView setRegion:region animated:YES];

 }
Bixby answered 2/3, 2013 at 18:13 Comment(1)
Thanks for the response. The zooming works but the zoomed region doesn't stay. It quickly reverts back to it's original zoom called in the didUpdateToLocation. I think because the didUpdateToLocation is continuously updating and calls the original span back when it runs. Do you have any ideas to fix this issue and have the zoomed view stay without it reverting back?Footer
F
1

OK, this is what I ended using to get my 2 IBAction buttons (zoomIn and zoomOut) to work with my mapView. When you open the app, the user location is displayed and the map zooms in and centers around it. You can then use the buttons to zoom in or zoom out by a factor of 2. *Big thanks to Rob who showed me the way.

.h

- (IBAction)zoomIn:(id)sender;

- (IBAction)zoomOut:(id)sender;

.m

@interface LocationViewController ()

@property (nonatomic) BOOL alreadySetZoomScale;

@end



-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
      fromLocation:(CLLocation *)oldLocation {


if (!_alreadySetZoomScale)
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate,    1609, 1609); // 1 mile = 1609.34 meters

self.mapView.region = region;
[self.mapView setRegion:region animated:YES];
_alreadySetZoomScale = YES;

}

else

{
[self.mapView setCenterCoordinate:newLocation.coordinate animated:YES];
}


- (IBAction)zoomIn:(id)sender {

MKCoordinateRegion region = self.mapView.region;
region.span.latitudeDelta /= 2.0;
region.span.longitudeDelta /= 2.0;
self.mapView.region = region;

}

- (IBAction)zoomOut:(id)sender {;

MKCoordinateRegion region = self.mapView.region;
region.span.latitudeDelta *= 2.0;
region.span.longitudeDelta *= 2.0;
self.mapView.region = region;

}

I have a couple other calls to MKMapView like self.mapView.showsUserLocation = YES; in viewDidLoad but that's pretty much it.

Footer answered 7/3, 2013 at 0:51 Comment(0)
C
0

Swift 2.0:

Using Extension :

extension MKMapView{

 func zoomInPinAnnotationLocation(targetMapViewName : MKMapView?, delta: Double)
 {
  var region: MKCoordinateRegion = targetMapViewName!.region
  region.span.latitudeDelta /= delta
  region.span.longitudeDelta /= delta
  targetMapViewName!.region = region

 }
 func zoomOutPinAnnotationLocation(targetMapViewName : MKMapView?,delta: Double)
 {
  var region: MKCoordinateRegion = targetMapViewName!.region
  region.span.latitudeDelta *= delta
  region.span.longitudeDelta *= delta
  targetMapViewName!.region = region
 }

}

Usage:

var mapViewZoomStepperValue: Double = -1.0
@IBOutlet weak var mapViewZoomStepper: UIStepper!

@IBAction func mapViewZoomStepperValueChanged(sender: AnyObject) {

  if (mapViewZoomStepper.value  > mapViewZoomStepperValue)
  {
   mapViewZoomStepperValue = mapViewZoomStepperValue + 1.0

//Zoom In
   detailMapView.zoomInPinAnnotationLocation(detailMapView, delta: 3.0)
  }
  else
  {
   mapViewZoomStepperValue = mapViewZoomStepper.value - 1.0

//Zoom Out
   detailMapView.zoomOutPinAnnotationLocation(detailMapView, delta: 3.0)
  }

 }
Centrifugal answered 29/3, 2016 at 11:3 Comment(0)
C
0

You could achieve the most basic in 3 lines of code

var region: MKCoordinateRegion = self.mapView.region
region = MKCoordinateRegionMake(self.mapView.centerCoordinate, MKCoordinateSpanMake(0.005, 0.005));
self.mapView.setRegion(region, animated: true)
Crewelwork answered 4/9, 2016 at 5:55 Comment(0)
C
0

Swift 3:

import GoogleMaps
import GooglePlaces
import CoreLocation
import UIKit

class LocationMapView: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate {

     @IBOutlet weak var mapView: GMSMapView!

     var mapViewZoomStepperValue: Float = 0.0
     @IBOutlet weak var mapViewZoomStepper: UIStepper!

     override func viewDidLoad() {
            super.viewDidLoad()

            mapViewZoomStepper.minimumValue = -100

        }

        @IBAction func mapViewZoomStepperValueChanged(_ sender: Any) {

            if (Float(mapViewZoomStepper.value) > mapViewZoomStepperValue){

                mapViewZoomStepperValue = Float(mapViewZoomStepper.value)
                zoomIn()

            }else{

                mapViewZoomStepperValue = Float(mapViewZoomStepper.value)
                zoomOut()

            }

        }

        func zoomIn() {

            print("Zoom in!!")
            if mapView.camera.zoom == mapView.maxZoom {
                return
            }

            let currentZoom = mapView.camera.zoom + 1
            mapViewZoomController(currentZoom)

        }

        func zoomOut() {

            print("Zoom out!!")
            if mapView.camera.zoom == mapView.minZoom {
                return
            }

            let currentZoom = mapView.camera.zoom - 1
            mapViewZoomController(currentZoom)

        }

        func mapViewZoomController(_ level: Float) {

            let point: CGPoint = mapView.center
            let coor: CLLocationCoordinate2D = mapView.projection.coordinate(for: point)
            let camera = GMSCameraPosition.camera(withLatitude: coor.latitude, longitude: coor.longitude, zoom: Float(level))
            mapView.animate(to: camera)

        }

}
Canonize answered 19/7, 2017 at 7:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.