Prevent scrolling outside map area on google map - ios

Am actually dealing with the Google Maps Framework for iOS, and I want to block scrolling out side a giving area.
What I tried to do at first, implement the delegate method : - (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position and compare the map position with the right/left corners.But what I did has many issues when scrolling or zooming.
Below an exemple of my implementation :
- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position {
if (position.target.latitude > topLat) {
GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:topLat
longitude:position.target.longitude
zoom:position.zoom
bearing:220
viewingAngle:0];
[self.mapView animateToCameraPosition:goBackCamera];
}
if (position.target.latitude < bottomLat) {
GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:bottomLat
longitude:position.target.longitude
zoom:position.zoom
bearing:220
viewingAngle:0];
[self.mapView animateToCameraPosition:goBackCamera];
}
if (position.target.longitude > rightLong) {
GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:position.target.latitude
longitude:rightLong
zoom:position.zoom
bearing:220
viewingAngle:0];
[self.mapView animateToCameraPosition:goBackCamera];
}
if (position.target.longitude < leftLong) {
GMSCameraPosition *goBackCamera = [GMSCameraPosition cameraWithLatitude:position.target.latitude
longitude:leftLong
zoom:position.zoom
bearing:220
viewingAngle:0];
[self.mapView animateToCameraPosition:goBackCamera];
}
}
Do you no a way more efficient to deal with this?
PS: TopLat, RightLong ... mean top Latitude and Right longitude etc
Regards

One way I can think of is by setting up the bound in the map in you want maps to scroll using GMSCoordinateBounds.
Next thing you can do is to set up the scroll gesture to true just within that section of maps you have specified above bounds. For the rest of the map the scroll gesture should be false.

Use GMSCoordinateBounds(northeast co-ordinates, southwest co-ordinates) method.
This might be helpful
GMSCoordinateBounds Class Reference

Related

Centering custom marker GMSMapView

I am trying to position my custom marker in iOS(Objective-C) with Google Maps.
Map view is all showing up fine with marker, however, moving the map will take the marker with it as its fetching current location. What I am looking for is the have it set to center of the map container, which will then fetch coordinates of map pin and display in search bar.
I am thinking I am doing this in reverse? I added the customer marker as a UIImage of the map container however it still does not show. Should I be getting the location from the position of the map marker, and then reverse geocoding address from there.
- (void)addPins:(float)lat andLng:(float)lng andName:(NSString*)strName withAddress:(NSString*)strAddr
{
// Creates a marker in the center of the map.
GMSMarker *marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(currentUserLocation.coordinate.latitude, currentUserLocation.coordinate.longitude);
NSLog(#"lat : %f, lng : %f", lat, lng);
marker.icon = [UIImage imageNamed:#"map_pin"];
marker.title = strName;
marker.snippet = strAddr;
marker.appearAnimation = kGMSMarkerAnimationPop;
marker.map = mapView;
}
The user needs to be able to search and set their address, besides their current location.
Thanks
Use your mapView delegate and use the responder to re-center your pin and re-fire your geocoordinate lookup.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
Edit: If GMS Maps does not have these delegates, the approach may work with using whatever [mapView didUpdate]callback they have documented.
You can set GMSCameraPosition for centering your marker
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:"YOUR LATITUDE"
longitude:"YOUR LONGITUDE"
zoom:centerZoom];
"YOUT GMSMAPVIEW" = [GMSMapView mapWithFrame:"YOUR VIEW".bounds camera:camera];

GMSMapView didTapAtCoordinate gives wrong coordinates until map is panned

I am working with a GMSMapView that I have added as a custom UITableViewCell. When I first create the map and capture an initial user tap, my coordinates are based as though the user tapped near lat/lon 0/0 -- I get values like -1.800144/-1.867676 even though my map center is set to 43.50 and -116.22 (and the map displays around those coordinates).
However, when I move the map by panning it once, and then tapping on it, my coordinates are more along the line of what I expect. Also, if I add the following code to the GMSMapViewDelegate method mapView:idleAtCameraPosition: I get the correct coordinates the first time:
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
GMSCameraUpdate *update = [GMSCameraUpdate setCamera:position] ;
[mapView moveCamera:update] ;
}) ;
But that seems like a hack that is hiding a more fundamental issue.
Here is the relevant code:
My custom table view cell creates the initial mapView in its constructor:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Camera position is moot, just need something to instantiate the map view
GMSCameraPosition * cameraPosition = [GMSCameraPosition cameraWithLatitude:0 longitude:0 zoom:5];
GMSMapView * mapView = [GMSMapView mapWithFrame:CGRectZero camera:cameraPosition];
_mapView = mapView ;
}
return self;
}
In my view controller, I set the gestures enabled, set my delegate and my map bounds:
- (void) configureMap
{
[_mapView.settings setAllGesturesEnabled:YES];
[_mapView.settings setConsumesGesturesInView:YES] ;
_mapView.delegate = self ;
// _userTrip has among other things, a bounds for our map
GMSCoordinateBounds * bounds = _userTrip.bounds ;
DDLogInfo(#"Trip Bounds are sw(%f:%f) ne(%f:%f)", bounds.southWest.latitude, bounds.southWest.longitude, bounds.northEast.latitude, bounds.northEast.longitude) ;
GMSCameraUpdate *cameraUpdate = [GMSCameraUpdate fitBounds:bounds];
[_mapView moveCamera:cameraUpdate];
}
My delegate for camera position:
- (void) mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position
{
DDLogInfo(#"User changed map size. Zoom is now set to %f", mapView.camera.zoom) ;
/* putting this in 'fixes' the problem
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
GMSCameraUpdate *update = [GMSCameraUpdate setCamera:position] ;
[mapView moveCamera:update] ;
}) ;
*/
}
My delegate method for taps:
- (void) mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate
{
DDLogInfo(#"User did tap at coordinate %f, %f", coordinate.latitude, coordinate.longitude) ;
DDLogInfo(#"Map view center %f %f and zoom is %f", mapView.camera.target.latitude, mapView.camera.target.longitude, mapView.camera.zoom) ;
}
Sample output:
[] Trip Bounds are sw(43.409890:-116.435511) ne(43.597180:-116.023860)
[] map view zoom = 10.053131
[] User changed map size. Zoom is now set to 10.053131
--- At this point, the map is displayed and it shows the right location based on the bounds in the log statement above. Then, when I tap on the map:
[] User did tap at coordinate -0.262352, -0.900879
[] Map view center 43.503608 -116.229685 and zoom is 10.053131
--- As you can see above, the user coordinates are way off based on where the map thinks it is. Below, I have panned the map and then did another tap
[] User changed map size. Zoom is now set to 10.053131
[] User did tap at coordinate 43.447926, -116.340871
[] Map view center 43.426753 -116.377271 and zoom is 10.053131
As you can see, the user coordinates now appear to be a valid location on the map. I have verified through the debugger that the same map instance is being referenced throughout and it has the same gestureRecognizer.
I had a previous version of the same code that did not put the GMSMapView inside a table view cell, and it does not exhibit the same problem. So, I suspect there is an initialization issue caused by putting this in a UITableViewCell.
Anyone run across something like this and have any idea how to fix it?

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 .

change the default location on google maps iOS sdk

it is my first time to use google map and i asked to let the user choose the location that he wanted to point by taping on the map. when the application start it will point on the device location how the user will change it to the location he wants?
First get the point of user's tap. Then, get coordinate value for that CGPoint and set it as mapView's center.
-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
// Get tap point
CGPoint tapPoint = [recognizer locationInView:[recognizer superView]];
// Convert CGPoint to CLLocationCoordinate2D
CLLocationCoordinate2D center = [self.mapView.projection coordinateForPoint:tapPoint];
// Set camera of mapView
GMSCameraPosition * camera = [GMSCameraPosition cameraWithLatitude:center.latitude longitude:center.longitude zoom:self.mapView.camera.zoom];
[self.mapView setCamera:camera];
}
Alternatively you can implement the GMSMapViewDelegate and use the - mapView:didTapAtCoordinate: method.
You can move the view port by doing something similar like this to move the center point of map view
GMSCameraPosition *sydney = [GMSCameraPosition cameraWithLatitude:-33.8683
longitude:151.2086
zoom:6];
[mapView_ setCamera:sydney];
(Google Map SDK called it camera position, as if you use a camera to target a portion of the whole map)
Checkout https://developers.google.com/maps/documentation/ios/views?hl=en Moving the camera section
So here, in order to finish your task with move use to the point where he tapped. You'll need to do following steps:
define a iVar or property to keep record of the tap point location(lat/lng):
CLLocationCoordinate2D *currentTapLocation;
In didTapAtCoordinate delegate, fetch that position
- (void) mapView:(GMSMapView *) mapView didTapAtCoordinate:(CLLocationCoordinate2D) coordinate{
currentTapLocation = coordinate;
}
Create a GMSCamera and set camera to currentTapLocation
- (void) mapView:(GMSMapView *) mapView didTapAtCoordinate:(CLLocationCoordinate2D) coordinate{
currentTapLocation = coordinate;
GMSCameraPosition *newCameraPosition = [GMSCameraPosition cameraWithLatitude:currentTapLocation.latitude
longitude:currentTapLocation.longitude
zoom:6];
[mapView_ setCamera:newCameraPosition];
}
Optional step: if you want animate to the new position instead setCamera directly,
you can do:
[mapView_ animateWithCameraUpdate:[mapView_
setCamera:newCameraPosition];];
instead of
[mapView_ setCamera:newCameraPosition];

Resources