Question

How do I prevent 'GMSMapView' on IOS 7 from Infinite horizontal scrolling? I am using a 'MKTileOverlay' and 'kGMSTypeNone' to show a custom image which stretches all over the world.

(When the user scrolls to the left the image is repeated again. I would like the scrolling to stop here.)

Was it helpful?

Solution 2

Hmm, my initial idea would be to use delegate method

- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture;

to move the camera back when your scrolling limit is reached. Using this, your map view may overshoot your limit temporarily (depending on the frequency of delegate method call) when user scrolls to the end, but will animate back to your desired limit.

We can go into further details if you think this approach suits you.

UPDATE:

OK, actually I realised you should use another delegate method:

- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position {

instead as it gives you constant position updates when moving. willMove is called only once before moving. Anyway, the idea is that you set up your map view and limits (bounding box), that would be e/g box of your overlay. I've just created an area around my location as an example and positioned initial camera position inside it. That's set up in viewDidLoad method.

Further, in didChangeCameraPosition method you

  1. reposition the marker position so you can see where you're currently pointing
  2. check if you've passed lat/long limits meaning you've passed bounds of your overlay and move/animate back

Mind that the code below doesn't check if you've passed through a corner (lat and long limit simultaneously), then you might end up off limits, but you can easily check for that condition with more if's.

Viewcontroller with mapview setup and delegate methods is below, and I've uploaded the complete project here: https://www.dropbox.com/s/1a599wowvkumaa8/LimitingMap.tar.gz . Please, don't forget to provide you API key in app delegate as I've removed mine from the code.

So, the view controller is as follows:

#import "ViewController.h"
#import <GoogleMaps/GoogleMaps.h>

@interface ViewController () <GMSMapViewDelegate> {
    CLLocationCoordinate2D center, topLeft, topRight, bottomLeft, bottomRight;
    double leftLong, rightLong, bottomLat, topLat;
    GMSMarker *currentPosition;
}

@property (weak, nonatomic) IBOutlet GMSMapView *mapView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // GMSMapview itself is wired in storyboard, just set delegate
    self.mapView.delegate = self;

    // Lat/long limits (bounding box)
    leftLong = 15.0;
    rightLong = 16.0;
    bottomLat  = 45.0;
    topLat  = 46.0;

    // center coordinate for map view and set it to mapview
    center = CLLocationCoordinate2DMake(45.895064, 15.858220);
    GMSCameraPosition *cameraPosition = [GMSCameraPosition cameraWithLatitude:center.latitude
                                                                    longitude:center.longitude
                                                                         zoom:10];
    self.mapView.camera = cameraPosition;

    // Current position, for displaying marker
    currentPosition = [GMSMarker markerWithPosition:center];
    currentPosition.map = self.mapView;

    // coordinates based on coordinate limits for bounding box drawn as polyline
    topLeft     = CLLocationCoordinate2DMake(topLat, leftLong);
    topRight    = CLLocationCoordinate2DMake(topLat, rightLong);
    bottomLeft  = CLLocationCoordinate2DMake(bottomLat, leftLong);
    bottomRight = CLLocationCoordinate2DMake(bottomLat, rightLong);


    // Create visual bounding box with fat polyline
    GMSMutablePath *path = [[GMSMutablePath alloc] init];
    [path addCoordinate:topLeft];
    [path addCoordinate:topRight];
    [path addCoordinate:bottomRight];
    [path addCoordinate:bottomLeft];
    [path addCoordinate:topLeft];

    GMSPolyline *polyLine = [GMSPolyline polylineWithPath:path];
    polyLine.strokeWidth = 10.0;
    polyLine.map = self.mapView;

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Google Maps iOS SDK delegate methods

- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position {


    // Reposition GMSMarker introduced in viewDidLoad to updated position
    currentPosition.position = position.target;

    // The interesting part - a non-elegant way to detect which limit was passed
    // If each of lat/long limits is passed, map will move or animate to limiting position

    if (position.target.latitude > topLat) { // If you scroll past upper latitude
        // Create new campera position AT upper latitude and current longitude (and zoom)
        GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:topLat
                                                                      longitude:position.target.longitude
                                                                           zoom:position.zoom];
        // Now, you can go back without animation,
        //self.mapView.camera = goBackCamera;

        // or with animation, as you see fit.
        [self.mapView animateToCameraPosition:goBackCamera];
    }

    if (position.target.latitude < bottomLat) {
        GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:bottomLat
                                                                      longitude:position.target.longitude
                                                                           zoom:position.zoom];
        //self.mapView.camera = goBackCamera;
        [self.mapView animateToCameraPosition:goBackCamera];
    }

    if (position.target.longitude > rightLong) {
        GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:position.target.latitude
                                                                      longitude:rightLong
                                                                           zoom:position.zoom];
        //self.mapView.camera = goBackCamera;
        [self.mapView animateToCameraPosition:goBackCamera];
    }

    if (position.target.longitude < leftLong) {
        GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:position.target.latitude
                                                                      longitude:leftLong
                                                                           zoom:position.zoom];
        //self.mapView.camera = goBackCamera;
        [self.mapView animateToCameraPosition:goBackCamera];
    }


}

@end

OTHER TIPS

Your code didn't work always, so I improved it (also swift version)

     func mapView(mapView: GMSMapView, didChangeCameraPosition position: GMSCameraPosition) {

        var latitude  = position.target.latitude;
        var longitude = position.target.longitude;

        if (position.target.latitude > bounds.northEast.latitude) {
            latitude = bounds.northEast.latitude;
        }

        if (position.target.latitude < bounds.southWest.latitude) {
            latitude = bounds.southWest.latitude;
        }

        if (position.target.longitude > bounds.northEast.longitude) {
            longitude = bounds.northEast.longitude;
        }

        if (position.target.longitude < bounds.southWest.longitude) {
            longitude = bounds.southWest.longitude;
        }

        if (latitude != position.target.latitude || longitude != position.target.longitude) {

            var l = CLLocationCoordinate2D();
            l.latitude  = latitude;
            l.longitude = longitude;

            mapView.animateToLocation(l);

        }
    } 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top