Google Maps API (GMS) map doesn't show up - ios

Im trying to make the map automatically follow the user (location) but somehow the app either it crashes (no error message) or the map doesn't show up when I start up the app. What have I done wrong?
I have tried without the locationManager func, and then it does work. Are there any other ways to follow the user?
class GMSTestViewController: BaseViewController, GMSMapViewDelegate {
#IBOutlet weak var mapView: GMSMapView!
let locationManager = CLLocationManager()
var manager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
//Setup Location Manager
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
//Map type
mapView.mapType = kGMSTypeTerrain
}
override func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation = locations.last
let center = CLLocationCoordinate2D(latitude: userLocation!.coordinate.latitude, longitude: userLocation!.coordinate.longitude)
let camera = GMSCameraPosition.cameraWithLatitude(userLocation!.coordinate.latitude,
longitude: userLocation!.coordinate.longitude, zoom: 8)
let mapView = GMSMapView.mapWithFrame(.zero, camera: camera)
mapView.myLocationEnabled = true
self.view = mapView
let marker = GMSMarker()
marker.position = center
marker.title = "Current Location"
marker.snippet = "XXX"
marker.map = mapView
locationManager.stopUpdatingLocation()
}
}
It looks like this when I start it up, and then it keeps blinking with the map (on the users location).

Check for these things :-
You have set the delegate of your GMSMapview #IBOutlet to self
mapView.delegate = self
You have updated your info.plist file with specific key's
You have a proper internet connection.
You have configured Google Maps in your AppDelegate
var googleApiKey = String(_yourAPIkey)
GMSServices.provideAPIKey(googleApiKey)

I believe the best way to do it is to use key-value observing (KVO):
override func viewWillAppear(animated: Bool) {
mapView.addObserver(self, forKeyPath: "myLocation", options:0, context:nil)
}
deinit {
mapView.removeObserver(self, forKeyPath:"myLocation", context:0)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if(keyPath! == "myLocation"]) {
let location = [object myLocation]
let target =
CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude);
mapView.animateToLocation(target)
mapView.animateToZoom(17)
}
}

It seems that google map is not appear in your view.I think you want to get view like in image.
So i suggest you to download Google map SDK version below 1.10 because I am facing this problem too many times.
I suggest you to download google map SDK 1.9.1 for google map from below link
https://developers.google.com/maps/documentation/ios-sdk/releases
Just try to use version 1.9.1 once. Hope this thing works
NOTE: If you want to use Google map's place auto complete and other new services then use higher versions of Google map SDK

Had the same problem.
Fixed by getting new API Key.
As I discovered, you need two keys: one for Google Places, one for Google Maps. At least for testing purpose.
In Google docs it doesn't mention.

Related

Can't get user's current location

I have a simple goal of my app that is get the current coordinate, and use them to add an annotation on the mapview.
I have been tried lots of solution from google results, but its still not working....
The debug area never shows "locationManager did UpdateLocation", the message what I print in function....
It's seems like the app never run "did UpdateLocation" function, even startUpdatingLocation() has been called?
Add location privacy string in info.plist : Done.
Turn on the GPS on my Mac Pro : Done.
Xcode version : 10.1
MacOS : 10.13.6 (High Sierra)
let cloaction = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
MapView.delegate = self
MapView.showsScale = true
MapView.showsPointsOfInterest = true
MapView.showsUserLocation = true
cloaction.requestAlwaysAuthorization()
cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
print("IN")
cloaction.delegate = self
cloaction.desiredAccuracy = kCLLocationAccuracyBest
cloaction.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager did UpdateLocation")
let location = CLLocationCoordinate2D(latitude: (locations.first?.coordinate.latitude)!, longitude: (locations.first?.coordinate.longitude)!)
currentLat = (locations.first?.coordinate.latitude)!
currentLon = (locations.first?.coordinate.longitude)!
let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
MapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2DMake(currentLat,currentLon), span: span), animated: true)
MapView.showsUserLocation = true
print(locations.first?.coordinate.latitude)
print(locations.first?.coordinate.longitude)
}
Actually the reason is very simple: You call the didUpdateLocation mehthod wich is only called when you change your location. Your Mac is on certain place and dont move so thats why it is not working.
Have you import CoreLocation?
Start with making a variable let myLocation = CLLocation()
Instead of have so much in viewDidLoad you can make a function and call the mLocation in viewDidLoad instead :
func mLocation(){
cloaction.delegate = self
cloaction.desiredAccuracy = kCLLocationAccuracyBest
cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled(){
cloaction.startUpdatingLocation()
}
}
And thats all you need for the clocation
LocationManager could also be updated
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations:[CLLocation]){
let newLocation = locations[0]
print("\(myLocation.coordinate.latitude)")
print("\(myLocation.coordinate.longitude)")
}
Yes! finally... thank your answering, it's really need to run on device, thanks Kosuke Ogawa's suggestion, and every one's guide, I am a new to learn swift, and first time ask question here, it's fun, thank you every one.
(But I don't know how to accept a answer if the answer is a comment? Someone teach me how do that?)

why CoreLocation unable to fetch new GPS data and Mapkit display this old GPS data

I am working on MapKit and core location to display my current GPS location.
The code is working ok but with some problems.
The problem is as follows:
I turn on my app in an open area like on the roadside. The app is able to get my current GPS location and display it on the Map.
I walk into a building with the app on. When inside, I launch the page to show the GPS location. It shows my previous GPS location. As I know, when I am inside the building, the Mapkit and Corelocation should not be able to get GPS. But in this case, it shows my previous GPS data!
I walk out of the building with the app on. In the open space I launch the page to show my current GPS location but the app is unable to get my new GPS location but displays the previous GPS data. In this case the app should fetch a new GPS location. I have to try a few times to launch the page (Navigate from GPS-VC to home-VC, from home click a button to launch the GPS-VC to get GPS).
Why is the GPS so slow even though I am in the open space with good signal strength?
Is there a difference to call these methods:
LocationManager.startUpdatingLocation()
LocationManager.requestLocation()
Here the code:
#IBOutlet weak var Map: MKMapView!
var locationMgr : CLLocationManager!
override func viewDidload(){
if(CLLocationManager.locationServicesEnabled() )
{
locationMgr = CLLocationManager()
locationMgr.delegate = self
locationMgr.desriedAccuracy = kCLLocationAccuracyBest
locationMgr.requestWhenInuseAuthorization()
locationMgr.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLoction: CLLocation = locations[0]
let latitude = userLoction.coordinate.latitude
let longitude = userLoction.coordinate.longitude
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)
let dropin = MKPointAnnotation()
dropin.coordinate = location
droping.title = "Here"
self.Map.setRegion(region, animated: true)
self.Map.addAnnotation(dropin)
self.Map.showsUserLocation = true
}
I'm not sure this will solve all 3 of your issues but I spotted the following issues in your delegate method:
the locations array is sorted in ascending time order so locations.last will contain the most recent location.
This method might be called with old cached values so you need to check the timestamp of the location to decide if it's worth using.

How to get exact current location for google maps with accuracy less than 1 meter, Swift 4?

I am working on construction project and for that I want to fetch exact current location which must satisfy the accuracy withing the 1 meter. I am using google maps with SDK "CLLocationManager" and I am getting the current location but the location is not exact, it has some (+/-)5 meters to (+/-)10 meters error in location. I want the exact/accurate current location which should not exceeds the location accuracy error more than a feet.
Please help me out to fetch EXACT CURRENT LOCATION.
Also, is there any third party library, any hardware device (which I can connect to iOS device.) or anything else, please let me know.
Your valuable comment will be most appreciate.
Edited:-
Here I am sharing my code to get the current location using CLLocationManager:
override func viewDidLoad()
{
super.viewDidLoad()
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let position = CLLocationCoordinate2D(latitude: manager.location!.coordinate.latitude, longitude: manager.location!.coordinate.longitude)
marker.position = position
print("position:",position)
}
Thank you..
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let location = manager.location else{
return
}
var currentLocationCoordinate = location.coordinate
}
using this delegate function you will get the current location
You can use external gps if you want to get the exact location, with the help of device you will always get this fluctuation. You can also set your location accuracy to best.
https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html
You should always relay on GPS for accurate location. You can set locationManager.desireAccuracy = kCLLocationAccuracyBest. It will call you locationManager didUpdateLocation with the location array with each location having its accuracy you can apply your logic here.
Be alert to call locationManager.stopUpdatingLocation() when you done with desire accuracy location.
e.g
let horizontalAccuracy: Double = 20.0
let howRecent = location.timestamp.timeIntervalSinceNow
guard CLLocationCoordinate2DIsValid(location.coordinate),
location.horizontalAccuracy > 0,
location.horizontalAccuracy < horizontalAccuracy,
abs(howRecent) < 10 else { return false }
return true
}

Background Location Updates not working on device alone, but works when device is linked to Xcode [Swift 3.0 - Xcode 8]

When the app is in the foreground, the location updates every second and works perfectly, which is what I want. But when the app is in the background (or in suspended state, I'm not sure), I do not get any local notifications(Ive tested the local notifications on the phone alone, so this isn't the problem). I use local notifications to tell me if the location has been updated, which is how I know the location isn't being updated.
I've set up the background capabilities and info.plist i.e "Privacy - Location When in Use Usage Description" etc. And I've added the below commands.
All my code is in view controllers, not appdelegate, is this my problem?
Note: this works in the background on simulator and on a real device when the device is linked to a laptop with Xcode, and the app is launched from Xcode. It does not work on a real device by itself.
class ViewController: UIViewController, CLLocationManagerDelegate{
let manager = CLLocationManager()
#IBOutlet weak var map: MKMapView!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01,0.01) //shows the size of map screen
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude,location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
self.map.showsUserLocation = true
print("Location Updated")
}
override func viewDidLoad() {
super.viewDidLoad()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
manager.delegate = self
manager.allowsBackgroundLocationUpdates = true
manager.pausesLocationUpdatesAutomatically = false
}
}
Can someone please help? This is one of the last issues I need to solve for one of my projects. Any links or working Github projects would be greatly appreciated! I just cant figure this one out!

Zoom in on user location in iOS

I've been trying to solve this for a few hours and I can't seem to arrive at a solution. I am trying to create a button on my map view that zooms in on the users location when pressed. Here is the code for the function that pertains to the button:
func zoomInOnLocation() {
let userLocation = MKUserLocation()
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
let currentLocation: CLLocation? = userLocation.location
let latitude = currentLocation?.coordinate.latitude
let longitude = currentLocation?.coordinate.longitude
let span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude!, longitude!)
let region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: true)
}
When I click the button in the simulator, I receive an error stating fatal error:
unexpectedly found nil while unwrapping an Optional value
with the fifth line mapDelegate.mapView!... highlighted in red. Also, I added the proper tag to Info.plist. Any help is much appreciated.
Check this :
if #available(iOS 9.0, *) {
locationManager.requestLocation()
} else {
// Fallback
}
let latitude:CLLocationDegrees = //insert latitutde
let longitude:CLLocationDegrees = //insert longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span = MKCoordinateSpanMake(latDelta, lonDelta)
let location = CLLocationCoordinate2DMake(latitude, longitude)
let region = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: false)
For more : Making the map zoom to user location and annotation (swift 2)
So you probably do not want to to interact with the mapView delegate as you are doing right now.
How about adding the delegates to the class of the view that holds the mapView, like this:
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
and setting the delegates in viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
mapView.delegate = self
}
And of course you want to have the locationManager and userLocation set up:
let locationManager = CLLocationManager()
var userLocation = CLLocation()
Notice that userLocation is a variable because most likely you would want to update it at some point.
This is how you probably want to work with locationManager:
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
You would make changes according to the needs of your app, and taking into account that the desired accuracy has an impact on battery life. Also, are you starting and stopping updates on userLocation? Because I do not see that in your code, unless you are doing it outside of this function.
A good practice is to try to minimize what an specific function does down to one task. You probably want to do all this setup elsewhere and then only zoom in inside the function. :)
Finally, in order to zoom in, you can change the values of MKCoordinateSpanMake, and remember that larger span values zoom in the map, so a smaller area is viewable.
let userLocationCoordinates = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.3, 0.3)
let region = MKCoordinateRegion(center: userLocationCoordinates, span: span)
mapView.setRegion(region, animated: true)
Hopefully that helps you out a bit, let me know how it goes!
Perhaps this will help. I created this function to zoom into an area defined by an array of positions, ranging from just the current user location out to the area around a set of points making up a polyline. The function provides for a buffer around the points based on a regionPaddingFactor constant set in my system constants.
func setRectView(_ locations: [MKAnnotation], mapView: MKMapView) // Size the area for display and reset the view
{
var maxLat = -90.0
var minLat = 90.0
var maxLon = -180.0
var minLon = 180.0
if locations.count >= 1 {
for waypoint in locations {
maxLat = max(maxLat, waypoint.coordinate.latitude)
minLat = min(minLat, waypoint.coordinate.latitude)
maxLon = max(maxLon, waypoint.coordinate.longitude)
minLon = min(minLon, waypoint.coordinate.longitude)
}
let loc = CLLocationCoordinate2DMake((maxLat-fabs(maxLat - minLat)/2), (maxLon-fabs(maxLon - minLon)/2))
let span = MKCoordinateSpanMake(0.001 + (1.0 + Setting.shared.regionPaddingFactor) * fabs(maxLat - minLat), 0.001 + (1.0 + Setting.shared.regionPaddingFactor) * fabs(maxLon-minLon))
// The 0.001 values above ensure that you do not get a 0.0 valued span if all of the points have the same latitude, longitude, or both, or if there is only one point
// The regionPaddingFactor is a constant to allow some space around the points passed in
let reg = MKCoordinateRegionMake(loc, span)
mapView.setRegion(reg, animated: true)
mapView.animatedZoom(zoomRegion: reg, duration: 0.8)
}
}
In the calling code, I provide for 3 settings that rotate as the user presses the button:
Show a tight view (just pass current location in the call)
Show the whole route (pass all points in the template)
Change to manual zooming / positioning
The last option is needed since I call the function whenever a new current position is received to reposition the view based on current location, which repeatedly refocuses the view if the user is trying to reposition the map.
If you don't want the flexibility of sending different position arrays, you can do the positioning here using mapView.annotations or just the current location as the array.

Resources