Google maps won't load, crashes the whole app - ios

I still have not solved this one yet.
I've worked with google maps in a few projects, both in Objective C and Swift 2 and iOS 9. I install it using cocoa pods and it works just fine but now, in this app where I'm also using parse and a four square API (not sure if that has anything to do with it) the map view won't even load and the app crashes when I try to.
This is the error I get...
-[GMSMapView animateToCameraPosition:]: unrecognized selector sent to instance 0x7fe51cb3bfb0
It breaks on the line that sets the map view frame, I've tried adding the Obj-C linker flag too (which I've heard could be a possible solution) but that gives me 32 different errors itself.
Can anyone help with this? Thanks a million.

Can't say I have experience with Google Maps in Swift, but here's some Obj-C code to work off of. First make sure your GMSMapView delegate is set to self and that you are setting the constraints in your viewDidLoad. I ran this in various spots:
- (void)setBounds
{
CLLocationCoordinate2D coordinate = [self.currentLocation coordinate];
CGFloat coordinateDifference = 0.002;
CGFloat firstLatitude = coordinate.latitude;
firstLatitude += coordinateDifference;
CGFloat firstLongitude = coordinate.longitude;
firstLongitude += coordinateDifference;
CLLocationDegrees topLat = firstLatitude;
CLLocationDegrees topLon = firstLongitude;
CLLocationCoordinate2D northEastCoordinate = CLLocationCoordinate2DMake(topLat, topLon);
CGFloat secondLatitude = coordinate.latitude;
secondLatitude -= coordinateDifference;
CGFloat secondLongitude = coordinate.longitude;
secondLongitude -= coordinateDifference;
CLLocationDegrees botLat = secondLatitude;
CLLocationDegrees botLon = secondLongitude;
CLLocationCoordinate2D southWestCoordinate = CLLocationCoordinate2DMake(botLat, botLon);
self.bounds = [[GMSCoordinateBounds alloc] init];
self.bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:northEastCoordinate coordinate:southWestCoordinate];
}
- (void)createMap
{
CLLocationCoordinate2D coordinate = [self.currentLocation coordinate];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:coordinate.latitude longitude:coordinate.longitude zoom:17];
CGFloat h = self.topLayoutGuide.length;
CGRect rect = CGRectMake(0, h, self.view.bounds.size.width, self.view.bounds.size.height - self.navigationController.navigationBar.frame.size.height - self.postSongButton.frame.size.height - 20);
self.mapView = [GMSMapView mapWithFrame:rect camera:camera];
[self.view insertSubview:self.mapView atIndex:0];
}

Related

Zooming in WKInterfaceMap with a slider (WKInterfaceSlider)

I have a slider on my map and I am trying to zoom in via the slider. Below is the code that I am using and when the + or - is hit the map just shows blue(water)
- (IBAction)sliderAction:(float)value {
RestaurantObject *restaurant = [nearbyMapArray objectAtIndex:0];
CLLocationCoordinate2D mapLocation = CLLocationCoordinate2DMake([restaurant.latitude doubleValue]*value, [restaurant.longitude doubleValue]*value);
MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(1, 1);
[self.map setRegion:(MKCoordinateRegionMake(mapLocation, coordinateSpan))];
}
You are probably passing values of latitude and longitude in wrong place.
try this , should work
- (IBAction)sliderAction:(UISlider*)slider
{
CGFloat value = slider.value; // slider.maximumValue - slider.value // if you want reverse effect
CGFloat sliderMax = slider.maximumValue;
CGFloat zoom = value / sliderMax;
RestaurantObject *restaurant = [nearbyMapArray objectAtIndex:0];
CLLocationCoordinate2D mapLocation = CLLocationCoordinate2DMake([restaurant.latitude doubleValue], [restaurant.longitude doubleValue]);
MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(1*zoom, 1*zoom);
[self.map setRegion:(MKCoordinateRegionMake(mapLocation, coordinateSpan))];
}

iOS Google Maps: Coordinates Mismatch

One of our application's feature is to plot a location on the map, we will save it in our server and show it on the main screen of the application. We are using Google Maps and GMSMarker with draggable property set to YES. But there is a difference between the marked location and the coordinate obtained.
Code:
NSDictionary *signDict = self.imgLocationsArray[_currentIndex];
_toSaveLat = [signDict[TAG_SIGN_LAT] floatValue];
_toSaveLon = [signDict[TAG_SIGN_LONG] floatValue];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:_toSaveLat longitude:_toSaveLon zoom:GOOGLE_MAPS_DEFAULT_ZOOM_LEVEL];
self.dragDropMap = [GMSMapView mapWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) camera:camera];
[self.dragDropMap setMapType:kGMSTypeNormal];
[self.dragDropMap setDelegate:self];
self.dragDropMap.myLocationEnabled = YES;
[self.view addSubview:self.dragDropMap];
if (draggingMarker) {
draggingMarker.map = nil;
draggingMarker = nil;
}
draggingMarker = [[GMSMarker alloc] init];
draggingMarker.icon = [GMSMarker markerImageWithColor:[UIColor plumColor]];//[UIImage imageNamed:#"MoveablePin"];
draggingMarker.position = CLLocationCoordinate2DMake(_toSaveLat, _toSaveLon);
draggingMarker.tappable = NO;
draggingMarker.draggable = YES;
draggingMarker.map = self.dragDropMap;
-(void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker{
NSLog(#"marker dragged to location: %f,%f", marker.position.latitude, marker.position.longitude);
_toSaveLat = marker.position.latitude;
_toSaveLon = marker.position.longitude;
}
Am I missing something in the code or anything else? Please suggest a solution or workaround to get this fixed. Thanks!
Testing Environment
xCode - 7.0
Google Maps SDK version - 1.10.3 (Using pod)
Device - iPhone 5S, iPhone 6plus
iOS - 9.0.2

SKMaps iOS: Pin disappears when panning the Map

With the new Version (2.1.0) I get a problem. The Annotation disappears when scrolling the map.
In the demo project it works fine. Also adding the frameworks again doesn't help.
- (void)viewDidLoad {
[super viewDidLoad];
placeDetail = [[PlaceDetailViewController alloc] init];
latitude = [placeDetail Latitude];
longitude = [placeDetail Longitude];
self.placeMapView = [[SKMapView alloc] init];
self.placeMapView.frame = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame));
self.placeMapView.delegate = self;
self.placeMapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.placeMapView.settings.poiDisplayingOption = SKPOIDisplayingOptionNone;
[self.view addSubview:self.placeMapView];
//add a circle overlay
for(int i=0;i<latitude.count;i++)
{
//set the map region
SKCoordinateRegion region;
region.center = CLLocationCoordinate2DMake([latitude[i] floatValue], [longitude[i] floatValue]);
region.zoomLevel = 17;
self.placeMapView.visibleRegion = region;
SKAnnotation *mapAnnotation = [SKAnnotation annotation];
mapAnnotation.identifier = i;
mapAnnotation.minZoomLevel = 5;
mapAnnotation.annotationType = SKAnnotationTypeRed;
mapAnnotation.location = CLLocationCoordinate2DMake([latitude[i] floatValue], [longitude[i] floatValue]);
[self.placeMapView addAnnotation:mapAnnotation];
}
}
Annoation creation
-(void)mapView:(SKMapView *)mapView didSelectAnnotation:(SKAnnotation *)annotation {
self.placeMapView.calloutView.titleLabel.text= placeDetail.Name;
self.placeMapView.calloutView.titleLabel.font = [UIFont fontWithName:#"PTSans-Narrow" size:15];
self.placeMapView.calloutView.subtitleLabel.text = #"";
[self.placeMapView showCalloutForAnnotation:annotation withOffset:CGPointMake(0, 42) animated:YES];
[self.placeMapView.calloutView.rightButton addTarget:self action:#selector(backToDetailView) forControlEvents:UIControlEventTouchUpInside];
}
I would appreciate your help.
EDIT: I think I found the problem that causes this beahvior. In my app I have two kind of maps. One Mini Map and one large Map. But in two different views.
When I deactivate the Mini Map it works. SO I think it has to do with the loading of the SKMap framework. Currently the mini map function is called in view did load method.
So you know what to do here?
It is a bug in the 2.1 and 2.2 versions of the SDK - it will be fixed in 2.3 (eta November 2014).
For 2.1/2.2 there is a workaround: having different id's for the annotations on the minimap and the big map. For example you have N annotations on the minimap with ids 0..N-1, then the big map would have annotations with ids from N onward. So for M annotations on the big map you would have annotations with ids from N..M-1.

Update region to be around Annotations

I have a mapView with some annotations and I'd like to center the map around the annotations. I have the following code:
- (void)updateRegion {
self.needUpdateRegion = NO;
CGRect boundingRect;
BOOL started = NO;
for (id <MKAnnotation> annotation in self.mapView.annotations){
CGRect annotationRect = CGRectMake(annotation.coordinate.longitude, annotation.coordinate.latitude, 0, 0);
if (!started) {
started = YES;
boundingRect = annotationRect;
} else {
boundingRect = CGRectUnion(boundingRect, annotationRect);
}
} if (started) {
boundingRect = CGRectInset(boundingRect, -0.2, -0.2);
if ((boundingRect.size.width >20) && (boundingRect.size.height >20)) {
MKCoordinateRegion region;
region.center.latitude = boundingRect.origin.x + boundingRect.size.width /2;
region.center.longitude = boundingRect.origin.y + boundingRect.size.height / 2;
region.span.latitudeDelta = boundingRect.size.width;
region.span.longitudeDelta = boundingRect.size.height;
[self.mapView setRegion:region animated:YES];
}
}
}
it gets executed in viewDidAppear to make the "sliding effect":
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.needUpdateRegion) [self updateRegion];
}
When I run the app it doesn't do anything and just shows the US.
The annotations are shown (in Europe).
Assuming that updateRegion gets called in the first place (make sure needUpdateRegion is initialized to YES), there are two main problems with the updateRegion method:
It only calls setRegion if the resulting bounding map rect's width and height are 20. Since you are doing the calculations using latitude and longitude degrees, this means setRegion will only get called if the resulting bounding map rect is more than 20 degrees latitude/longitude wide/high. It's not clear if this is what you intended.
The region properties are being set backwards. In the calculation of the bounding map rect, the x values are set to the longitude and the y values are set to the latitude. But when setting region.center.latitude, it is using boundingRect.origin.x instead of boundingRect.origin.y. This applies to the other properties as well so the code there should be:
region.center.longitude = boundingRect.origin.x + boundingRect.size.width /2;
region.center.latitude = boundingRect.origin.y + boundingRect.size.height / 2;
region.span.longitudeDelta = boundingRect.size.width;
region.span.latitudeDelta = boundingRect.size.height;
Note that iOS 7 provides a new convenient method showAnnotations:animated: to automatically show the annotations so you don't have to calculate the region yourself.
So in updateRegion you could do the following:
- (void)updateRegion {
self.needUpdateRegion = NO;
//if showAnnotations:animated: is available, use it...
if ([mapView respondsToSelector:#selector(showAnnotations:animated:)])
{
[self.mapView showAnnotations:mapView.annotations animated:YES];
return;
}
//calculate region manually...
}

How to setRegion with google maps sdk for iOS?

How to setRegion with google maps sdk for iOS?
I want set zoom for location and radius of markers.
UPDATE:
The original answer below is obsolete as of version 1.2 of the SDK - you can now use the fitBounds: method of the GMSCameraUpdate class:
https://developers.google.com/maps/documentation/ios/reference/interface_g_m_s_camera_update
Original answer:
The MKMapPoint type in MapKit defines a 2D projection of a map. Although the actual values of the projection are meant to be opaque, they turn out to be equivalent to pixels at zoom level 20. This can be used to convert lat/lon values to pixels, and therefore a scale, and therefore a zoom level.
Start by defining two locations which specify the bounds of the region you want to display. These could be opposite corners of the bounding box, or just two locations, for example:
CLLocationCoordinate2D location1 =
CLLocationCoordinate2DMake(-33.8683, 151.2086); // Sydney
CLLocationCoordinate2D location2 =
CLLocationCoordinate2DMake(-31.9554, 115.8585); // Perth
If you have more than two points that you want to include, you could calculate the bounds of them yourself. This can also be done using GMSCoordinateBounds, for example:
GMSCoordinateBounds* bounds =
[[GMSCoordinateBounds alloc]
initWithCoordinate: CLLocationCoordinate2DMake(-33.8683, 151.2086) // Sydney
andCoordinate: CLLocationCoordinate2DMake(-31.9554, 115.8585)]; // Perth
bounds = [bounds including:
CLLocationCoordinate2DMake(-12.4667, 130.8333)]; // Darwin
CLLocationCoordinate2D location1 = bounds.southWest;
CLLocationCoordinate2D location2 = bounds.northEast;
Next, you need to get the size of the map view in points. You could use this:
float mapViewWidth = _mapView.frame.size.width;
float mapViewHeight = _mapView.frame.size.height;
But that will only work if you've already created the map view. Also, if you're using the sample code in the getting started guide, the frame is set to CGRectZero, as the actual size will be set later to fill the screen. In these cases if you're creating a full-screen map then you might want something like this:
float mapViewWidth = [UIScreen mainScreen].applicationFrame.size.width;
float mapViewHeight = [UIScreen mainScreen].applicationFrame.size.height;
Otherwise, use the size which you're creating your map view with.
Now you have the info necessary to calculate the camera position:
MKMapPoint point1 = MKMapPointForCoordinate(location1);
MKMapPoint point2 = MKMapPointForCoordinate(location2);
MKMapPoint centrePoint = MKMapPointMake(
(point1.x + point2.x) / 2,
(point1.y + point2.y) / 2);
CLLocationCoordinate2D centreLocation = MKCoordinateForMapPoint(centrePoint);
double mapScaleWidth = mapViewWidth / fabs(point2.x - point1.x);
double mapScaleHeight = mapViewHeight / fabs(point2.y - point1.y);
double mapScale = MIN(mapScaleWidth, mapScaleHeight);
double zoomLevel = 20 + log2(mapScale);
GMSCameraPosition *camera = [GMSCameraPosition
cameraWithLatitude: centreLocation.latitude
longitude: centreLocation.longitude
zoom: zoomLevel];
You can then initialize the map view with this camera, or set the map view to this camera.
For this code to compile, you will need to add the MapKit framework to your project, and then also import it:
#import <MapKit/MapKit.h>
Note that this code doesn't handle wrap-around if your coordinates span across the date line. For example if you tried using this code with Tokyo and Hawaii, instead of displaying an area of the Pacific, it will try to display almost the entire world. In portrait mode it's not possible to zoom out far enough to see Hawaii on the left and Tokyo on the right, and so the map ends up centred on Africa with neither location visible. You could modify the above code to handle the wrap-around at the date line if you wanted to.
UPDATE
All issues were fixed in the latest version of Google maps (1.5). Standard method [mapView_ animateWithCameraUpdate:[GMSCameraUpdate fitBounds:bounds]]; can noow be used instead of the code below
ORIGINAL ANSWER
[GMSCameraUpdate fitBounds] does not give accurate results in my version of the SDK (1.2.0). I am using the code below instead of it. The formulae are taken from the Mercator Projection. The world is latitudonally bounded at 85 degrees as per Google Documentation.
#import <stdlib.h>
-(void) animateBoundsNorth:(CGFloat)north West:(CGFloat)west South:(CGFloat)south East:(CGFloat)east Padding:(int)padding {
CGFloat northRad = north * M_PI / 180.0;
CGFloat northProj = logf(tanf(M_PI_4 + northRad/2.0));
CGFloat southRad = south * M_PI / 180.0;
CGFloat southProj = logf(tanf(M_PI_4 + southRad/2.0));
CGFloat topRad = 85.0 * M_PI / 180.0;
CGFloat topProj = logf(tanf(M_PI_4 + topRad/2.0));
CGFloat zoomLat = log2f((mapView_.bounds.size.height - padding * 2) * 2 * topProj /(northProj - southProj)) - 8;
CGFloat zoomLon = log2f((mapView_.bounds.size.width - padding * 2) * 360/(east - west)) - 8;
GMSCameraUpdate *update = [GMSCameraUpdate setTarget:CLLocationCoordinate2DMake((north+south)/2.0, (west+east)/2.0) zoom:MIN(zoomLat, zoomLon)];
[mapView_ animateWithCameraUpdate:update];
}
Saxun Druce's answer is really good. But in addition, if you want to calculate a radius from any location you can do that with the following code:
CLLocationCoordinate2D center = CLLocationCoordinate2DMake([currentLocationLat doubleValue],[currentLocationLong doubleValue]);
float radius = 25*1000; //radius in meters (25km)
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, radius*2, radius*2);
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta/2, region.center.longitude - region.span.longitudeDelta/2);
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta/2, region.center.longitude + region.span.longitudeDelta/2);
GMSCoordinateBounds* bounds = [[GMSCoordinateBounds alloc]
initWithCoordinate: northEast
andCoordinate: southWest];
If you have latitude and longitude of 'far-left' and 'near-right' corners of the google map ,you can display the data in swift using below code
var region:GMSVisibleRegion = GMSVisibleRegion()
region.nearLeft = CLLocationCoordinate2DMake(nearleflat, nearleftlong)
region.farRight = CLLocationCoordinate2DMake(fareastlat,fareastlong)
let bounds = GMSCoordinateBounds(coordinate: region.nearLeft,coordinate: region.farRight)
let camera = googleMapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
googleMapView.camera = camera;
This link may also be helpful for related things.
I currently using this method.
self.markers is a dictionary with markers stored by a location ID, self.currentLocation is a CLLocation2D, and self.mapView is a GMSMapView.
The maths here is a check on whether to match the sizes on the width or the height, and then a calculation of the zoom based on the fact that x1 / pow(2, zoom1) = x2 / pow(2, zoom2)", leading to zoom2 = log2(x2 * pow(2, self.mapView.camera.zoom) / x1).
- (void)fitMarkers
{
if (2 > self.markers.count)
{
[self.mapView animateToCameraPosition:[GMSCameraPosition cameraWithTarget:self.currentLocation.coordinate zoom:kZoom]];
return;
}
NSArray* markers = self.markers.allValues;
GMSCoordinateBounds* markerBounds = [[GMSCoordinateBounds alloc] initWithCoordinate:((id<GMSMarker>)markers[0]).position andCoordinate:((id<GMSMarker>)markers[1]).position];
for (id<GMSMarker> marker in markers)
{
markerBounds = [markerBounds including:marker.position];
}
// get marker bounds in points
CGPoint markerBoundsTopLeft = [self.mapView.projection pointForCoordinate:CLLocationCoordinate2DMake(markerBounds.northEast.latitude, markerBounds.southWest.longitude)];
CGPoint markerBoundsBottomRight = [self.mapView.projection pointForCoordinate:CLLocationCoordinate2DMake(markerBounds.southWest.latitude, markerBounds.northEast.longitude)];
// get user location in points
CGPoint currentLocation = [self.mapView.projection pointForCoordinate:self.currentLocation.coordinate];
CGPoint markerBoundsCurrentLocationMaxDelta = CGPointMake(MAX(fabs(currentLocation.x - markerBoundsTopLeft.x), fabs(currentLocation.x - markerBoundsBottomRight.x)), MAX(fabs(currentLocation.y - markerBoundsTopLeft.y), fabs(currentLocation.y - markerBoundsBottomRight.y)));
// the marker bounds centered on self.currentLocation
CGSize centeredMarkerBoundsSize = CGSizeMake(2.0 * markerBoundsCurrentLocationMaxDelta.x, 2.0 * markerBoundsCurrentLocationMaxDelta.y);
// inset the view bounds to fit markers
CGSize insetViewBoundsSize = CGSizeMake(self.mapView.bounds.size.width - kMarkerSize / 2.0 - kMarkerMargin, self.mapView.bounds.size.height - kMarkerSize / 2.0 - kMarkerSize);
CGFloat x1;
CGFloat x2;
// decide which axis to calculate the zoom level with by comparing the width/height ratios
if (centeredMarkerBoundsSize.width / centeredMarkerBoundsSize.height > insetViewBoundsSize.width / insetViewBoundsSize.height)
{
x1 = centeredMarkerBoundsSize.width;
x2 = insetViewBoundsSize.width;
}
else
{
x1 = centeredMarkerBoundsSize.height;
x2 = insetViewBoundsSize.height;
}
CGFloat zoom = log2(x2 * pow(2, self.mapView.camera.zoom) / x1);
GMSCameraPosition* camera = [GMSCameraPosition cameraWithTarget:self.currentLocation.coordinate zoom:zoom];
[self.mapView animateToCameraPosition:camera];
}
As per new release of GoogleMaps iOS sdk 1.9.2,
We can set up using Camera's position, zoom level, viewingAngle.
GMSCameraPosition* camera = [GMSCameraPosition cameraWithLatitude:28.6100
longitude:77.2300
zoom:14.0
bearing:0
viewingAngle:0.00];
self.mapView = [GMSMapView mapWithFrame:CGRectMake(0, 45, self.view.frame.size.width, self.view.frame.size.height - 45) camera:camera];
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
mapView.delegate = self;
mapView.myLocationEnabled = YES;
mapView.mapType = kGMSTypeTerrain;
mapView.settings.compassButton = YES;
mapView.settings.myLocationButton = YES;
[self.mapView setMinZoom:10 maxZoom:18];
GMSMarker* marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(28.6100, 77.2300);
marker.title = #"New Delhi";
marker.snippet = #"Capital Of India";
marker.map = self.mapView;
marker.appearAnimation = kGMSMarkerAnimationPop;
marker.icon = [GMSMarker markerImageWithColor:[UIColor grayColor]];
[self.view addSubview:self.mapView];
For Further reference see this PDF document.
you can also set minimum and maximum Zoom level as per your need:
[self.mapView setMinZoom:10 maxZoom:30];
Hope this solves the problem.
I searched through the header files of the framework and only found the interface that could be used for the following code which could be a start. The problem here is that i can not find any imports of GMSCoordinateBounds in one of the other headers so i can not find a way to display this region in GMSMapView.
GMSVisibleRegion region;
region.farLeft = CLLocationCoordinate2DMake(farLeftLatitude, farLeftlongitude);
region.farRight = CLLocationCoordinate2DMake(farRightlatitude, farRightlongitude);
GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:region];
As of June 2014, this answer is the simplest way to iterate over a given array of markers and then set bounds accordingly.
// I have found this method that worked for me
func setMapZoomToRadius(lat:Double, lng:Double, var mile:Double)
{
let center = CLLocationCoordinate2DMake(lat, lng)
let radius: Double = (mile ) * 621.371
let region = MKCoordinateRegionMakeWithDistance(center, radius * 2.0, radius * 2.0)
let northEast = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta, region.center.longitude - region.span.longitudeDelta)
let southWest = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta, region.center.longitude + region.span.longitudeDelta)
print("\(region.center.longitude) \(region.span.longitudeDelta)")
let bounds = GMSCoordinateBounds(coordinate: southWest, coordinate: northEast)
let camera = googleMapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
googleMapView.camera = camera;
}

Resources