How to wrap swiftUI view into UIViewControllerRepresentable, so that Google maps camera focuses on user's location at startup? - cllocationmanager

Im currently trying to display an instance of Google Maps that focuses on a users location at startup using SwiftUI.
To display the map I call my GoogMapView() view in my main view file. It just sets its camera to focus on Boston and drops a marker on Boston at startup.
Code for GoogMapView.swift here:
import SwiftUI
//import MapKit
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation
struct GoogMapView : UIViewRepresentable {
// private let locationManager = CLLocationManager()
let marker : GMSMarker = GMSMarker()
//Creates a `UIView` instance to be presented.
func makeUIView(context: Context) -> GMSMapView {
// Create a GMSCameraPosition
let camera = GMSCameraPosition.camera(withLatitude: 42.361145, longitude: -71.057083, zoom: 16.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.setMinZoom(14, maxZoom: 20)
mapView.settings.compassButton = true
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.settings.rotateGestures = true
mapView.settings.tiltGestures = true
mapView.isIndoorEnabled = false
if let mylocation = mapView.myLocation {
print("User's location: \(mylocation)")
} else {
print("User's location is unknown")
}
// locationManager.desiredAccuracy = kCLLocationAccuracyBest
// locationManager.requestAlwaysAuthorization()
// locationManager.distanceFilter = 50
// locationManager.startUpdatingLocation()
// locationManager.delegate = self
return mapView
}
// Updates the presented `UIView` (and coordinator) to the latestconfiguration.
func updateUIView(_ mapView: GMSMapView, context: Context) {
// Creates a marker in the center of the map.
marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
marker.title = "Boston"
marker.snippet = "USA"
marker.map = mapView
}
}
My commented out code above is my attempt at having the GMSMapView camera focus on the users device location at startup (like the google developer page says) but i keep getting the error "Cannot assign value of type GoogMapView to type CLLocationManagerDelegate? on the line with locationManager.delegate = self
I've read that I need to utilize UIViewControllerRepresentable to wrap my above code in order to fix this issue. Here's an example of wrapping someone pointed me to for reference:
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
#Binding var currentPage: Int
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[controllers[currentPage]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController?
{
guard let index = parent.controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return parent.controllers.last
}
return parent.controllers[index - 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController?
{
guard let index = parent.controllers.firstIndex(of: viewController) else {
return nil
}
if index + 1 == parent.controllers.count {
return parent.controllers.first
}
return parent.controllers[index + 1]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = parent.controllers.firstIndex(of: visibleViewController)
{
parent.currentPage = index
}
}
}
}
Does anyone know how i can get my map in GoogMapView.swift to center on a user's location at startup by wrapping it in a UIViewController??

Git for anyone who needs this:
//
// GoogMapView.swift
// Landmarks
//
//
import SwiftUI
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation
struct GoogMapView: View {
var body: some View {
GoogMapControllerRepresentable()
}
}
class GoogMapController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var mapView: GMSMapView!
let defaultLocation = CLLocation(latitude: 42.361145, longitude: -71.057083)
var zoomLevel: Float = 15.0
let marker : GMSMarker = GMSMarker()
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude, longitude: defaultLocation.coordinate.longitude, zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
mapView.setMinZoom(14, maxZoom: 20)
mapView.settings.compassButton = true
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.settings.rotateGestures = true
mapView.settings.tiltGestures = true
mapView.isIndoorEnabled = false
// if let mylocation = mapView.myLocation {
// print("User's location: \(mylocation)")
// } else {
// print("User's location is unknown")
// }
marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
marker.title = "Boston"
marker.snippet = "USA"
marker.map = mapView
// Add the map to the view, hide it until we've got a location update.
view.addSubview(mapView)
// mapView.isHidden = true
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude, longitude: location.coordinate.longitude, zoom: zoomLevel)
if mapView.isHidden {
mapView.isHidden = false
mapView.camera = camera
} else {
mapView.animate(to: camera)
}
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
struct GoogMapControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
return GMController()
}
func updateUIViewController(_ uiViewController: GMController, context: UIViewControllerRepresentableContext<GMControllerRepresentable>) {
}
}

Related

How to fix Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value?

I have a problem with the Users Location.
When im trying to build the program it gets this error code: (Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value)
My code:
import UIKit
import MapKit
class ViewController: UIViewController {
private let locationManager = CLLocationManager()
private var currentCoordinate: CLLocationCoordinate2D?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
configerLocationServices()
}
private func configerLocationServices() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdates(locationManager: locationManager)
}
}
private func beginLocationUpdates(locationManager: CLLocationManager) {
mapView.showsUserLocation = true //<--- the problem is here
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
private func zoomToLastestLocation(with coordinate: CLLocationCoordinate2D) {
let zoomRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: 10000, longitudinalMeters: 10000)
mapView.setRegion(zoomRegion, animated: true)
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest Location")
guard let latestLocation = locations.first else { return }
if currentCoordinate == nil {
zoomToLastestLocation(with: latestLocation.coordinate)
}
currentCoordinate = latestLocation.coordinate
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("The Status changed")
if status == .authorizedAlways || status == .authorizedWhenInUse {
self.beginLocationUpdates(locationManager: manager)
}
}
}
I don't know what im doing wrong, has anyone the solution?
Thank you in advance.
private func setNewLoaction(lat:CLLocationDegrees,long:CLLocationDegrees,markerTitle:String){
let center = CLLocationCoordinate2D(latitude: lat, longitude: long)
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: 15)
self.googleMapsView?.camera = camera
self.googleMapsView?.isMyLocationEnabled = true
let marker = GMSMarker(position: center)
marker.map = self.googleMapsView
marker.title = markerTitle
locationManager.stopUpdatingLocation()
}
//MARK:- MAP VIEW
private func setUpMap(){
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
self.googleMapsView = GMSMapView (frame: CGRect(x: 0, y: 0, width: self.view.frame.width-30, height: self.mapView.frame.height))
self.googleMapsView?.settings.compassButton = true
self.googleMapsView?.isMyLocationEnabled = true
self.googleMapsView?.settings.myLocationButton = true
self.mapView.addSubview(self.googleMapsView!)
}
I have called setUpMap in ViewDidload and this setLoaction function in GMSAutocompleteViewControllerDelegate =:
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
destinationField.text = place.formattedAddress
destinationLatitude = place.coordinate.latitude
destinationLongitutude = place.coordinate.longitude
setNewLoaction(lat: destinationLatitude!, long: destinationLongitutude!, markerTitle: "Destination Location")
dismiss(animated: true, completion: nil)
}
you can call this anywhere as per you need, do remember to turn on location when asked for permission and if using in simlulator go to Features/Loaction/custom Location
I have now an other code for the same thing it is shorter than the old one.
Here is the code:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
#IBOutlet var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true // <--- Crash
}
}
And now it gets the same problem as the old one :(

How to get nearby gas station and show direction to user with swift?

For a school project, I'm trying to get all nearby gas stations and create annotations for them and when the user click on one of these, I would like to get him the direction to go there.
this is the Maps controller that I implement by searching on internet and tutoriels:
import UIKit
import MapKit
import CoreLocation
protocol MapsControllerDelegate : class {
func mapsViewControllerDidSelectAnnotation(mapItem :MKMapItem)
}
class MapsController : UIViewController {
#IBOutlet weak var maps: MKMapView!
weak var delegate :MapsControllerDelegate!
let locationManager = CLLocationManager()
let regionInMeters: Double = 1000
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager(){
locationManager.delegate = self as! CLLocationManagerDelegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerViewOnUserLocation(){
if let location = locationManager.location?.coordinate{
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
maps.setRegion(region, animated: true)
}
}
func checkLocationServices(){
if CLLocationManager.locationServicesEnabled(){
//setup the location manager.
setupLocationManager()
checkLocationAuthorization()
}
else{
//Show alert let the user know how to do it.
}
}
func checkLocationAuthorization(){
switch CLLocationManager.authorizationStatus(){
case .authorizedWhenInUse:
maps.showsUserLocation = true
centerViewOnUserLocation()
locationManager.startUpdatingLocation()
case .denied:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
break
case .authorizedAlways:
break
}
}
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
let annotationView = views.first!
if let annotation = annotationView.annotation {
if annotation is MKUserLocation {
centerViewOnUserLocation()
populateNearByPlaces()
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let annotation = view.annotation as! PlaceAnnotation
self.delegate.mapsViewControllerDidSelectAnnotation(mapItem: annotation.mapItem)
}
func populateNearByPlaces(){
print("Im heeeeeerrrrreeeeeee")
if let location = locationManager.location?.coordinate{
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "Gas Station"
request.region = region
let search = MKLocalSearch(request: request)
search.start { (response, error) in
guard let response = response else {
return
}
for item in response.mapItems {
print("I'm here")
print(item)
let annotation = PlaceAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
annotation.mapItem = item
DispatchQueue.main.async {
self.maps.addAnnotation(annotation)
}
}
}
}
}
}
extension MapsController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {return}
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion.init(center: center, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
maps.setRegion(region, animated: true)
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
}
}
I don't have any errors or the app stops working but I'm not getting the results expected. In the Maps scene I'm just getting the user's actual location on the map and that's it.

How to make form sheet appear after Google Maps marker infoWindow is tapped in iOS?

I'm currently building an app using SwiftUI and Google Maps. I'm trying to get a form sheet to appear after a Google Maps marker's infoWindow is tapped, but I'm having trouble getting it working.
In other parts of my app I display sheets using this method:
Example method here
I tried using the same method above to display a sheet after a marker's infoWindow is tapped, but am having trouble doing it from within a function. My code snippets below give more detail.
-
Below is a stripped down version of my GMView.swift file which controls my instance of Google Maps. (My file looks different from typical Swift + Google maps integration because im using SwiftUI).
You'll notice 3 main parts of the file: 1. the view, 2. the GMController class, and 3. the GMControllerRepresentable struct:
import SwiftUI
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation
struct GoogMapView: View {
var body: some View {
GoogMapControllerRepresentable()
}
}
class GoogMapController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate {
var locationManager = CLLocationManager()
var mapView: GMSMapView!
let defaultLocation = CLLocation(latitude: 42.361145, longitude: -71.057083)
var zoomLevel: Float = 15.0
let marker : GMSMarker = GMSMarker()
override func viewDidLoad() {
super.viewDidLoad()
// Control location data
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
}
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude, longitude: defaultLocation.coordinate.longitude, zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
mapView.setMinZoom(14, maxZoom: 20)
mapView.settings.compassButton = true
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.settings.rotateGestures = true
mapView.settings.tiltGestures = true
mapView.isIndoorEnabled = false
marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
marker.title = "Boston"
marker.snippet = "USA"
marker.map = mapView
// view.addSubview(mapView)
mapView.delegate = self
self.view = mapView
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
struct GoogMapControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
return GMController()
}
func updateUIViewController(_ uiViewController: GMController, context: UIViewControllerRepresentableContext<GMControllerRepresentable>) {
}
}
This is the function I've added to the GMController class in my GMView.swift file above that Google's documentation says to use to handle when a marker's infoWindow is tapped:
// Function to handle when a marker's infowindow is tapped
func mapView(_ mapView: GMSMapView, didTapInfoWindowOf didTapInfoWindowOfMarker: GMSMarker) {
print("You tapped a marker's infowindow!")
// This is where i need to get the view to appear as a modal, and my attempt below
let venueD2 = UIHostingController(rootView: VenueDetail2())
venueD2.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height - 48)
self.view.addSubview(venueD2.view)
return
}
My function above currently displays a view when the infowindow is tapped, but it just appears over my google maps view, so I dont get the animation nor am I able to dismiss the view like a typical iOS form sheet.
Does anyone know how I can display a sheet after a Google maps marker infowindow is tapped in SwiftUI instead of just adding it as a subview?
Hi there to interact with UIViewController from a struct View you need to bind a variable.. fist we declare #Binding var isClicked : Bool and If you need to pass more parameter to the struct you need to declare it with announcement #Binding. any way in UIViewController an error will show that isClicked Property 'self.isClicked' not initialized to fix this we declare:
#Binding var isClicked
init(isClicked: Binding<Bool>) {
_isClicked = isClicked
super.init(nibName: nil, bundle: nil)
}
also The designated initialiser for UIViewController is initWithNibName:bundle:. You should be calling that instead. If you don't have a nib, pass in nil for the nibName (bundle is optional too).
now we have all setup for the UIViewController and we move to UIViewControllerRepresentable: the same what we did at first we need to declare the #Binding var isClicked because the viewController will request a new parameter at initializations so we will have something like this:
#Binding var isClicked: Bool
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
return GMController(isClicked: $isClicked)
}
in the struct View:
#State var isClicked: Bool = false
var body: some View {
GoogMapControllerRepresentable(isClicked: $isClicked)
.sheet(isPresented: $isShown) { () -> View in
<#code#>
}
}
and one more thing we just need to toggle this variable on marker click like this:
func mapView(_ mapView: GMSMapView, didTapInfoWindowOf didTapInfoWindowOfMarker: GMSMarker) {
print("You tapped a marker's infowindow!")
// This is where i need to get the view to appear as a modal, and my attempt below
self.isClicked.toggle()
// if you want to pass more parameters you can set them from here like self.info = //mapView.coordinate <- Example
return
}

How to "Show my current location on google maps, when I open the ViewController?" in Swift?

I am using Google maps sdk of iOS(Swift).
Has anyone know how to "Show my current location on google maps, when I open the ViewController"?
Actually it just like Google Maps App. When you open the Google Maps, the blue spot will show your current location. You don't need press the "myLocationButton" in first time.
So this is the code:
import UIKit
import CoreLocation
import GoogleMaps
class GoogleMapsViewer: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
let locationManager = CLLocationManager()
let didFindMyLocation = false
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.cameraWithLatitude(23.931735,longitude: 121.082711, zoom: 7)
let mapView = GMSMapView.mapWithFrame(CGRectZero, camera: camera)
mapView.myLocationEnabled = true
self.view = mapView
// GOOGLE MAPS SDK: BORDER
let mapInsets = UIEdgeInsets(top: 80.0, left: 0.0, bottom: 45.0, right: 0.0)
mapView.padding = mapInsets
locationManager.distanceFilter = 100
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
// GOOGLE MAPS SDK: COMPASS
mapView.settings.compassButton = true
// GOOGLE MAPS SDK: USER'S LOCATION
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
}
}
// MARK: - CLLocationManagerDelegate
extension GoogleMapsViewer: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
locationManager.startUpdatingLocation()
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 20, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
}
}
}
Anyone help? Thank you so much!
For Swift 3.x solution, please check this Answer
First all of you have to enter a key in Info.plist file
NSLocationWhenInUseUsageDescription
After adding this key just make a CLLocationManager variable and do the following
#IBOutlet weak var mapView: GMSMapView!
var locationManager = CLLocationManager()
class YourControllerClass: UIViewController,CLLocationManagerDelegate {
//Your map initiation code
let mapView = GMSMapView.mapWithFrame(CGRectZero, camera: camera)
self.view = mapView
self.mapView?.myLocationEnabled = true
//Location Manager code to fetch current location
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
//Location Manager delegates
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let camera = GMSCameraPosition.cameraWithLatitude((location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 17.0)
self.mapView?.animateToCameraPosition(camera)
//Finally stop updating location otherwise it will come again and again in this delegate
self.locationManager.stopUpdatingLocation()
}
When you run the code you will get a pop up of Allow and Don't Allow for location. Just click on Allow and you will see your current location.
Make sure to do this on a device rather than simulator. If you are using simulator, you have to choose some custom location and then only you will be able to see the blue dot.
Use this code,
You miss the addObserver method and some content,
viewDidLoad:
mapView.settings.compassButton = YES;
mapView.settings.myLocationButton = YES;
mapView.addObserver(self, forKeyPath: "myLocation", options: .New, context: nil)
dispatch_async(dispatch_get_main_queue(), ^{
mapView.myLocationEnabled = YES;
});
Observer Method:
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if change[NSKeyValueChangeOldKey] == nil {
let location = change[NSKeyValueChangeNewKey] as CLLocation
gmsMap.camera = GMSCameraPosition.cameraWithTarget(location.coordinate, zoom: 16)
}
}
hope its helpful
first add the following to your info.plist
NSLocationWhenInUseUsageDescription
LSApplicationQueriesSchemes (of type array and add two items to this array
item 0 : googlechromes ,
item 1 : comgooglemaps
second go to https://developers.google.com/maps/documentation/ios-sdk/start and follow the steps till step 5
last thing to do after you set up every thing is to go to your ViewController and paste the following
import UIKit
import GoogleMaps
class ViewController: UIViewController,CLLocationManagerDelegate {
//Outlets
#IBOutlet var MapView: GMSMapView!
//Variables
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
initializeTheLocationManager()
self.MapView.isMyLocationEnabled = true
}
func initializeTheLocationManager() {
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var location = locationManager.location?.coordinate
cameraMoveToLocation(toLocation: location)
}
func cameraMoveToLocation(toLocation: CLLocationCoordinate2D?) {
if toLocation != nil {
MapView.camera = GMSCameraPosition.camera(withTarget: toLocation!, zoom: 15)
}
}
}
( don't forget to add a view in the storyboard and connect it to the MapViw)
now you can build and run to see your current location on the google map just like when you open the Google Map App
enjoy coding :)
Swift 3.0 or above
For displaying user location (Blue Marker) in GMS map View make sure you have got Location Permission and add this line
mapView.isMyLocationEnabled = true
You can use RxCoreLocation:
import UIKit
import GoogleMaps
import RxCoreLocation
import RxSwift
class MapViewController: UIViewController {
private var mapView: GMSMapView?
private let disposeBag = DisposeBag()
private let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude: 0, longitude: 0, zoom: 17.0)
mapView = GMSMapView.map(withFrame: .zero, camera: camera)
view = mapView
manager.rx
.didUpdateLocations
.subscribe(onNext: { [weak self] in
guard let location = $0.locations.last else { return }
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude, longitude: location.coordinate.longitude, zoom: 17.0)
self?.mapView?.animate(to: camera)
self?.manager.stopUpdatingLocation()
})
.disposed(by: disposeBag)
}
}
SwiftUI
struct GoogleMapView: UIViewRepresentable {
#State var coordinator = Coordinator()
func makeUIView(context _: Context) -> GMSMapView {
let view = GMSMapView(frame: .zero)
view.isMyLocationEnabled = true
view.animate(toZoom: 18)
view.addObserver(coordinator, forKeyPath: "myLocation", options: .new, context: nil)
}
func updateUIView(_ uiView: GMSMapView, context _: UIViewRepresentableContext<GoogleMapView>) {}
func makeCoordinator() -> GoogleMapView.Coordinator {
return coordinator
}
static func dismantleUIView(_ uiView: GMSMapView, coordinator: GoogleMapView.Coordinator) {
uiView.removeObserver(coordinator, forKeyPath: "myLocation")
}
final class Coordinator: NSObject {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if let location = change?[.newKey] as? CLLocation, let mapView = object as? GMSMapView {
mapView.animate(toLocation: location.coordinate)
}
}
}
}
after the line:
view = mapView
add:
mapView.isMyLocationEnabled = true
This will enable your location:
NOTE:- Locations on Simulator are preset for particular places, you can not change them. if you want to use current location you have to use real device for testing.
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate,GMSMapViewDelegate {
#IBOutlet weak var currentlocationlbl: UILabel!
var mapView:GMSMapView!
var locationManager:CLLocationManager! = CLLocationManager.init()
var geoCoder:GMSGeocoder!
var marker:GMSMarker!
var initialcameraposition:GMSCameraPosition!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.mapView = GMSMapView()
self.geoCoder = GMSGeocoder()
self.marker = GMSMarker()
self.initialcameraposition = GMSCameraPosition()
// Create gms map view------------->
mapView.frame = CGRect(x: 0, y: 150, width: 414, height: 667)
mapView.delegate = self
mapView.isMyLocationEnabled = true
mapView.isBuildingsEnabled = false
mapView.isTrafficEnabled = false
self.view.addSubview(mapView)
// create cureent location label---------->
self.currentlocationlbl.lineBreakMode = NSLineBreakMode.byWordWrapping
self.currentlocationlbl.numberOfLines = 3
self.currentlocationlbl.text = "Fetching address.........!!!!!"
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
if locationManager.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization))
{
self.locationManager.requestAlwaysAuthorization()
}
self.locationManager.startUpdatingLocation()
if #available(iOS 9, *)
{
self.locationManager.allowsBackgroundLocationUpdates = true
}
else
{
//fallback earlier version
}
self.locationManager.startUpdatingLocation()
self.marker.title = "Current Location"
self.marker.map = self.mapView
// Gps button add mapview
let gpbtn:UIButton! = UIButton.init()
gpbtn.frame = CGRect(x: 374, y: 500, width: 40, height: 40)
gpbtn.addTarget(self, action: #selector(gpsAction), for: .touchUpInside)
gpbtn.setImage(UIImage(named:"gps.jpg"), for: .normal)
self.mapView.addSubview(gpbtn)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
var location123 = CLLocation()
location123 = locations[0]
let coordinate:CLLocationCoordinate2D! = CLLocationCoordinate2DMake(location123.coordinate.latitude, location123.coordinate.longitude)
let camera = GMSCameraPosition.camera(withTarget: coordinate, zoom: 16.0)
self.mapView.camera = camera
self.initialcameraposition = camera
self.marker.position = coordinate
self.locationManager.stopUpdatingLocation()
}
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition)
{
self.currentAddres(position.target)
}
func currentAddres(_ coordinate:CLLocationCoordinate2D) -> Void
{
geoCoder.reverseGeocodeCoordinate(coordinate) { (response, error) in
if error == nil
{
if response != nil
{
let address:GMSAddress! = response!.firstResult()
if address != nil
{
let addressArray:NSArray! = address.lines! as NSArray
if addressArray.count > 1
{
var convertAddress:AnyObject! = addressArray.object(at: 0) as AnyObject!
let space = ","
let convertAddress1:AnyObject! = addressArray.object(at: 1) as AnyObject!
let country:AnyObject! = address.country as AnyObject!
convertAddress = (((convertAddress.appending(space) + (convertAddress1 as! String)) + space) + (country as! String)) as AnyObject
self.currentlocationlbl.text = "\(convertAddress!)".appending(".")
}
else
{
self.currentlocationlbl.text = "Fetching current location failure!!!!"
}
}
}
}
}
}

How to Stop getting the users location in MapBox

How can you stop getting the user location when using CLLocationManager and mapbox?
I have a application that does the following:
1) Gets the users current location with the CLLocationManager and then calls the command ".stopUpdatingLocation()" which stops getting the user location.
2) Creates a map with mapbox
As soon as the application has both, it does NOT stop getting the user location.
I tested the application in the each separate scenarios (option 1 above alone and option 2 alone) and it successfully stop getting the user location but when the application has both implemented it does NOT stop getting the user location.
viewController.swift:
import UIKit
import MapboxGL
import CoreLocation
class ViewController: UIViewController, MGLMapViewDelegate , CLLocationManagerDelegate {
//MARK: - Properties
var manager: CLLocationManager?
private var currentLocation: CLPlacemark?
private var currLocLatitude:CLLocationDegrees?
private var currLocLongitude:CLLocationDegrees?
private var currLocTitle:String?
private var currLocSubtitle:String?
private var MapBoxAccessToken = "AccessToken.GoesHere"
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager()
manager?.delegate = self
manager?.desiredAccuracy = kCLLocationAccuracyBest
manager?.requestWhenInUseAuthorization()
manager?.startUpdatingLocation()
}
//MARK: - Helper
/* gather location information */
func getLocationInfo(placemark: CLPlacemark) {
currentLocation = placemark //will delete later - redudant
currLocLatitude = placemark.location.coordinate.latitude
currLocLongitude = placemark.location.coordinate.longitude
currLocTitle = placemark.areasOfInterest[0] as? String
currLocSubtitle = placemark.locality
//DEBUGGING
print(placemark.location.coordinate.latitude)
print(placemark.location.coordinate.longitude)
print(placemark.areasOfInterest[0])
print(placemark.locality)
}
//MARK: - CLLocationManagerDelegate
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
manager.stopUpdatingLocation()
let location = locations[0] as? CLLocation
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error) -> Void in
if (error != nil) {
println("ERROR:" + error.localizedDescription)
return
}
if placemarks.count > 0 {
var currLocation = placemarks[0] as! CLPlacemark
self.getLocationInfo(currLocation)
self.createMapBoxMap()
} else {
print("Error with data")
}
})
}
func locationManager( manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print(" Authorization status changed to \(status.rawValue)")
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError) {
print("Error:" + error.localizedDescription)
}
//MARK: - MapBox Methods
private func createMapBoxMap(){
//type of map style
let mapView = MGLMapView(frame: view.bounds, accessToken: MapBoxAccessToken)
//dark map style
// let mapView = MGLMapView(frame: view.bounds, accessToken: "pk.eyJ1IjoibHVvYW5kcmUyOSIsImEiOiI4YzAyOGMwOTAwMmQ4M2U5MTA0YjliMjgxM2RiYzk0NSJ9.isuNZriXdmrh-n9flwTY9g",styleURL: NSURL(string: "asset://styles/dark-v7.json"))
mapView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
//setting the map's center coordinate
mapView.setCenterCoordinate(CLLocationCoordinate2D(latitude: currLocLatitude!, longitude: currLocLongitude!),
zoomLevel: 25, animated: false)
view.addSubview(mapView)
/*define the marker and its coordinates, title, and subtitle:*/
mapView.delegate = self // Set the delegate property of our map view to self after instantiating it.
// Declare the marker `ellipse` and set its coordinates, title, and subtitle
let ellipse = MyAnnotation(location: CLLocationCoordinate2D(latitude: currLocLatitude!, longitude: currLocLongitude!),
title: currLocTitle!, subtitle: currLocSubtitle!)
mapView.addAnnotation(ellipse) // Add marker `ellipse` to the map
}
//MARK: - MGLMapViewDelegate
/* defining the marker from MyAnnotation.swift */
func mapView(mapView: MGLMapView!, symbolNameForAnnotation annotation: MGLAnnotation!) -> String! {
return "secondary_marker"
}
/* Tapping the marker */
func mapView(mapView: MGLMapView!, annotationCanShowCallout annotation: MGLAnnotation!) -> Bool {
return true
}
}
AppDelegate.swift:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
MyAnnotation.swift:
import Foundation
import MapboxGL
class MyAnnotation: NSObject, MGLAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String!
var subtitle: String!
init(location coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
You are calling the manager returned in the function, try call self.manager.stopUpdatingLocation()
Resolved this issue by getting the user location inside the "ViewDidLoad" method and creating the map inside the "ViewDidAppear" method
By having them seperated, it seems to have resolve the problem.
import UIKit
import CoreLocation
import MapboxGL
class AViewController: UIViewController, CLLocationManagerDelegate {
var manager:CLLocationManager!
var userLocation:CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
getUserLocation()
}//eom
override func viewDidAppear(animated: Bool) {
createMapBoxMap()
}
/*getting user current location*/
func getUserLocation(){
self.manager = CLLocationManager()
self.manager.delegate = self
self.manager.desiredAccuracy = kCLLocationAccuracyBest
self.manager.requestWhenInUseAuthorization()
self.manager.startUpdatingLocation()
}//eom
/*location manager 'didUpdateLocations' function */
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
self.manager.stopUpdatingLocation() //stop getting user location
println(locations)
self.userLocation = locations[0] as! CLLocation
}//eom
/*Create preliminary map */
func createMapBoxMap(){
// set your access token
let mapView = MGLMapView(frame: view.bounds, accessToken: "pk.eyJ1IjoiZGFya2ZhZGVyIiwiYSI6IlplVDhfR3MifQ.pPEz732qS8g0WEScdItakg")
mapView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
// set the map's center coordinate
mapView.setCenterCoordinate(CLLocationCoordinate2D(latitude: self.userLocation.coordinate.latitude, longitude: self.userLocation.coordinate.longitude),
zoomLevel: 13, animated: false)
view.addSubview(mapView)
//showing the user location on map - blue dot
mapView.showsUserLocation = true
}//eom

Resources