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
Related
I have a situation where I have to call an API to fetch some Vehicles Locations objects in an array after getting the user current location. After fetching vehicles, I have to get the address also from Vehicles Locations data, so for 'n' Vehicles, there will be an 'n' API call and then add annotations on Map.
After that, I have to refresh the Vehicles data every 1 min. So, I created a timer but even after getting the API response, annotations are not displaying on map. Kindly look into this issue.
Below is Map View
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet private var mapView: MKMapView!
var currentLocation: CLLocation?
var user: User?
lazy var vehicleViewModel = {
VehicleViewModel()
}()
var locationUpdateTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
configureLocationManager()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopTimer()
}
func configureLocationManager() {
LocationManager.shared().delegate = self
LocationManager.shared().initializeLocationManager()
}
func configureTimer() {
if locationUpdateTimer == nil {
locationUpdateTimer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(runLocationTimer), userInfo: nil, repeats: true)
}
}
#objc func runLocationTimer() {
fetchVehiclesLocation()
}
func resetMap() {
let annotations = mapView.annotations
mapView.removeAnnotations(annotations)
mapView = nil
}
func initializeMapView() {
mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
mapView.delegate = self
}
func configureMapView() {
let mapDetail = vehicleViewModel.getLatitudeLongitudeLatitudeDeltaLongitudeDelta()
if let latitude = mapDetail.0, let longitude = mapDetail.1, let latitudeDelta = mapDetail.2, let longitudeDelta = mapDetail.3 {
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), latitudinalMeters: latitudeDelta, longitudinalMeters: longitudeDelta)
let scaledRegion: MKCoordinateRegion = mapView.regionThatFits(region)
mapView.setRegion(scaledRegion, animated: true)
mapView.setCameraBoundary(
MKMapView.CameraBoundary(coordinateRegion: region),
animated: true)
let zoomRange = MKMapView.CameraZoomRange(maxCenterCoordinateDistance: 100000)
mapView.setCameraZoomRange(zoomRange, animated: true)
mapView.register(
VehicleAnnotationView.self,
forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
}
}
func fetchVehiclesLocation() {
configureTimer()
initViewModel {
DispatchQueue.main.async {
self.resetMap()
self.initializeMapView()
self.configureMapView()
}
if let user = self.user {
self.vehicleViewModel.fetchVehicleAddress(user: user, completion: { status in
if self.vehicleViewModel.vehicleAnnotationItems.count == 0 {
self.alertWithTitleAndMessageWithOK(("Alert" , "error while fetching vehicle locations"))
} else {
DispatchQueue.main.async {
self.mapView.addAnnotations(self.vehicleViewModel.vehicleAnnotationItems)
}
}
})
}
}
}
func initViewModel(completion: #escaping () -> Void) {
if let user = self.user, let userId = user.userId {
vehicleViewModel.getVehiclesLocation(userId: userId) { (vehicleApiResponse, error) in
if vehicleApiResponse != nil {
completion()
} else {
self.alertWithTitleAndMessageWithOK(("Alert" , error?.localizedDescription ?? "error while fetching vehicles"))
}
}
}
}
func stopTimer() {
if locationUpdateTimer != nil {
locationUpdateTimer!.invalidate()
locationUpdateTimer = nil
}
}
deinit {
stopTimer()
}
}
//MARK: - LocationManagerDelegate methods
extension MapViewController: LocationManagerDelegate {
func didFindCurrentLocation(_ location: CLLocation) {
currentLocation = location
if let currentLocation = currentLocation, (currentLocation.horizontalAccuracy >= 0) {
mapView.showsUserLocation = true
fetchVehiclesLocation()
}
}
}
LocationManager Extension class
import CoreLocation
protocol LocationManagerDelegate: AnyObject {
func didFindCurrentLocation(_ location: CLLocation)
func didFailedToFindCurrentLocationWithError(_ error: NSError?)
func alertLocationAccessNeeded()
}
/**
This class acts as a Singleton for getting location manager updates across the application.
*/
class LocationManager: NSObject {
var manager: CLLocationManager!
private static var sharedNetworkManager: LocationManager = {
let networkManager = LocationManager()
return networkManager
}()
private override init() {
super.init()
manager = CLLocationManager()
}
class func shared() -> LocationManager {
return sharedNetworkManager
}
weak var delegate: LocationManagerDelegate?
//Entry point to Location Manager. First the initialization has to be done
func initializeLocationManager() {
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.distanceFilter = kCLDistanceFilterNone
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.allowsBackgroundLocationUpdates = false
startUpdating()
}
//Start updating locations
func startUpdating() {
manager.startUpdatingLocation()
}
//Check for whether location services are disabled.
func locationServicesEnabled() -> Bool {
let isAllowed = CLLocationManager.locationServicesEnabled()
return isAllowed
}
}
//MARK: - CLLocation Manager delegate methods
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
manager.stopUpdatingLocation()
delegate?.didFindCurrentLocation(location)
// manager.delegate = nil
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
delegate?.didFailedToFindCurrentLocationWithError(error as NSError?)
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .notDetermined:
self.manager.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse, .authorizedAlways:
if locationServicesEnabled() {
self.startUpdating()
}
case .restricted, .denied:
delegate?.alertLocationAccessNeeded()
#unknown default:
print("Didn't request permission for location access")
}
}
}
Your code has a number of problems.
Neither your initializeMapView() function nor your resetMap() function make any sense.
You should add an MKMapView to your Storyboard, then connect it to your mapView outlet, and then don't assign a new value to mapView. Don't set it to nil, and don't replace the map view in the outlet with a brand new map view you create (like you're doing in initializeMapView().) Both of those things will prevent your map from displaying.
You also never create a timer except in your fetchVehiclesLocation() function, which doesn't seem right.
You also don't show how you're setting up your location manager and asking for location updates. (You call a function initializeLocationManager(). I don't believe that is an Apple-provided function. I'm guessing you added it in an extension to the location manager, but you don't show that code.)
You need to ask if the user has granted permission to use the location manager, and trigger a request for permission if not.
Once you have permission to use the location manager, you need to ask it to start updating the user's location.
You don't show any of that code.
Maybe you're doing that in code you didn't show? It also looks like you don't have your CLLocationManagerDelegate methods defined correctly. I don't know of any delegate method didFindCurrentLocation(_:). You will likely need to implement one of the delegate methods locationManager(_:didUpdateLocations:) or locationManager(_:didUpdateTo:from:).
I suggest searching for a tutorial on using the location manager to display the user's location on a map. It's a little involved, and requires some study to set up.
I want to draw over a map while walking on the street to record my path in SwiftUI.
I have a locationManager:
class LocationManager: NSObject, ObservableObject {
let locationManager = CLLocationManager()
let geoCoder = CLGeocoder()
#Published var knownLocations: [CLLocation] = []
#Published var location: CLLocation?
#Published var placemark: CLPlacemark?
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func geoCode(with location: CLLocation) {
geoCoder.reverseGeocodeLocation(location) { (placemark, error) in
if error != nil {
print(error!.localizedDescription)
} else {
self.placemark = placemark?.first
}
}
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
knownLocations.append(location)
DispatchQueue.main.async {
self.location = location
self.geoCode(with: location)
}
}
}
That contains an array of knownLocations, every time I move the didUpdateLocations is triggered correctly and saves the coordinates into the array.
My ContentView looks like this:
struct ContentView: View {
#ObservedObject var locationManager: LocationManager = LocationManager()
var body: some View {
MapView(knowLocations: locationManager.knownLocations)
.edgesIgnoringSafeArea(.all)
}
}
Which works fine, every time a new coordinate is added, this is called and it creates a new MapView with the added coordinate.
And finally, this is how my MapView looks:
struct MapView: UIViewRepresentable {
let mapView = MKMapView()
let knowLocations: [CLLocation]
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
addOverlays()
return mapView
}
func updateUIView(_ view: MKMapView, context: UIViewRepresentableContext<MapView>) {
addOverlays()
mapView.delegate = context.coordinator
}
private func addOverlays() {
let p = MKPolygon(coordinates: knowLocations.map({$0.coordinate}), count: knowLocations.count)
mapView.addOverlay(p)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
super.init()
}
func mapView(_: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
return renderer
}
}
}
When a new MapView is created with the array, it calls addOverlays() which creates a polygon with all the coordinates. I have checked and until here it's all correctly. The first time addOverlays() its called, the renderFor overlay is also called correctly but after that it stops being called. This means that no path is created. If I mocked knowLocations and at the beginning it contains some fake locations, the path is drawed correctly.
What am I doing wrong? Can I only call renderFor overlay once? Should I just do it every X minutes rather than live?
func updateUIView(_ view: MKMapView, context: UIViewRepresentableContext<MapView>) {
//addOverlays()
let p = MKPolygon(coordinates: knowLocations.map({$0.coordinate}), count: knowLocations.count)
view.addOverlay(p)
view.delegate = context.coordinator
}
I watched a few lessons on Youtube and now tried to get hands-on experience. I am pursuing a little project which involves a tabbed app where I tried on the first page to create a map with a button showing the current location. Pretty basic actually. But somehow it just doesn’t work and I don’t know what’s the issue. Someone from CodewithChris told me this “I would suggest breaking up your app into smaller components and making sure each one works before going on to the next. Try outputting your location first before plotting it on a map etc so you can localize bugs easier.” I just don’t understand what she means by smaller components. I really appreciate all the help I can get. Below is the code as good as possible. Thanks in advance for your help.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet var textFieldForAddress: UITextField!
#IBOutlet var getDirectionsButton: UIButton!
#IBOutlet var map: MKMapView!
var locationManger = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManger.delegate = self
locationManger.desiredAccuracy = kCLLocationAccuracyBest
locationManger.requestAlwaysAuthorization()
locationManger.requestWhenInUseAuthorization()
locationManger.startUpdatingLocation()
map.delegate = self
}
#IBAction func getDirectionsTapped(_ sender: Any) {
getAddress()
}
func getAddress() {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(textFieldForAddress.text!) { (placemarks, error) in
guard let placemarks = placemarks, let location = placemarks.first?.location
else {
print("No Location Found")
return
}
print(location)
self.mapThis(destinationCord: location.coordinate)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
}
func mapThis(destinationCord : CLLocationCoordinate2D) {
let souceCordinate = (locationManger.location?.coordinate)!
let soucePlaceMark = MKPlacemark(coordinate: souceCordinate)
let destPlaceMark = MKPlacemark(coordinate: destinationCord)
let sourceItem = MKMapItem(placemark: soucePlaceMark)
let destItem = MKMapItem(placemark: destPlaceMark)
let destinationRequest = MKDirections.Request()
destinationRequest.source = sourceItem
destinationRequest.destination = destItem
destinationRequest.transportType = .automobile
destinationRequest.requestsAlternateRoutes = true
let directions = MKDirections(request: destinationRequest)
directions.calculate { (response, error) in
guard let response = response else {
if let error = error {
print("Something is wrong :(")
}
return
}
let route = response.routes[0]
self.map.addOverlay(route.polyline)
self.map.setVisibleMapRect(route.polyline.boundingMapRect, animated: true)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let render = MKPolylineRenderer(overlay: overlay as! MKPolyline)
render.strokeColor = .blue
return render
}
}
If you want to show the user location and have the map track it, you need to set two properties on the map view - showsUserLocation and userTrackingMode. In order for the map to have access to the user location you must receive whenInUse authorisation from the user, using CLocationManager.
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet var textFieldForAddress: UITextField!
#IBOutlet var getDirectionsButton: UIButton!
#IBOutlet var map: MKMapView!
var locationManger = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManger.requestWhenInUseAuthorization()
map.showsUserLocation = true
map.userTrackingMode = .follow
}
If you are running on the simulator then you need to simulate a location using the Debug menu in the simulator.
Okay, this information is surprising hard to find (just getting your own location!) — even after watching tutorials I had a hard time. But basically what you're missing is mapView.showsUserLocation = true
Here's the full code, if you need it...
import UIKit
import CoreLocation
import MapKit
class RadiusMapLocationViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
let coordinate = CLLocationCoordinate2DMake(33.97823607957177, -118.43823725357653)
var locationManager : CLLocationManager = CLLocationManager()
// Authorize use of location
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
mapView.showsUserLocation = (status == .authorizedAlways)
}
// Entering region
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
showAlert(withTitle: "You've entered \(region.identifier)", message: "Happy hopping!")
}
// Exiting region
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
showAlert(withTitle: "You've exited \(region.identifier)", message: "")
}
// Creating region and notifying when exit / enter
func region(with geotification: Geotification) -> CLCircularRegion {
let region = CLCircularRegion(center: geotification.coordinate,
radius: geotification.radius,
identifier: geotification.identifier)
region.notifyOnEntry = (geotification.eventType == .onEntry)
region.notifyOnExit = !region.notifyOnEntry
return region
}
// Monitoring region, if not "error"
func startMonitoring(geotification: Geotification) {
if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
showAlert(withTitle:"Error", message: "Geofencing is not supported on this device!")
return
}
}
func stopMonitoring(geotification: Geotification) {
for region in locationManager.monitoredRegions {
guard let circularRegion = region as? CLCircularRegion,
circularRegion.identifier == geotification.identifier else { continue }
locationManager.stopMonitoring(for: circularRegion)
}
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.userTrackingMode = .follow
mapView.showsUserLocation = true
// Region of coordinate
mapView.region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 800, longitudinalMeters: 800)
mapView.region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
let title = "Marina Bar Hop"
let restaurantAnnotation = MKPointAnnotation()
restaurantAnnotation.coordinate = coordinate
restaurantAnnotation.title = title
mapView.addAnnotation(restaurantAnnotation)
let regionRadius = 300.0
let circle = MKCircle(center: coordinate, radius: regionRadius)
mapView.addOverlay(circle)
self.locationManager.requestAlwaysAuthorization()
self.locationManager.delegate = self as? CLLocationManagerDelegate
//Zoom to user location
if let userLocation = locationManager.location?.coordinate {
let viewRegion = MKCoordinateRegion(center: userLocation, latitudinalMeters: 200, longitudinalMeters: 200)
mapView.setRegion(viewRegion, animated: false)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.red
circleRenderer.lineWidth = 1.0
return circleRenderer
}
}
I have a few additional features on here if you need them. And it sounds like maybe you want to add a pin on the user's current location? It's included in this code too. I hope this helps! :)
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!!!!"
}
}
}
}
}
}
i am trying to provide the user with a navigation direction with the click of a button. But for some reason it doesn't seem to be working.
#IBAction func directionToDestination(sender: AnyObject) {
getDirections()
}
func getDirections(){
let request = MKDirectionsRequest()
let destination = MKPlacemark(coordinate: CLLocationCoordinate2DMake(place.latitude, place.longitude), addressDictionary: nil)
request.setSource(MKMapItem.mapItemForCurrentLocation())
request.setDestination(MKMapItem(placemark: destination))
request.transportType = MKDirectionsTransportType.Automobile
var directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler({(response:
MKDirectionsResponse!, error: NSError!) in
if error != nil {
// Handle error
} else {
self.showRoute(response)
}
})
}
func showRoute(response: MKDirectionsResponse) {
for route in response.routes as! [MKRoute] {
placeMap.addOverlay(route.polyline,level: MKOverlayLevel.AboveRoads)
for step in route.steps {
println(step.instructions)
}
}
}
func mapView(mapView: MKMapView!, rendererForOverlay
overlay: MKOverlay!) -> MKOverlayRenderer! {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blueColor()
renderer.lineWidth = 5.0
return renderer
}
here is how my viewDidLoad() looks
manager = CLLocationManager()
manager.delegate = self
manager.requestWhenInUseAuthorization()
placeMap.delegate = self
can someone please point what am i doing wrong with a sample code in swift ?
Here is a full working sample for getting the users location and getting directions to a destination coordinate.
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
mapView.showsUserLocation = true
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
#IBAction func directionToDestinationButtonPressed(_ sender: UIButton) {
guard let userLocationCoordinate = UserLocation.shared.location?.coordinate else { return }
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(
placemark: MKPlacemark(
coordinate: userLocationCoordinate
)
)
directionRequest.destination = MKMapItem(
placemark: MKPlacemark(
coordinate: CLLocationCoordinate2D(latitude: 47.6205, longitude: -122.3493)
)
)
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate { (response, error) in
guard let response = response else { return }
let route = response.routes.first
if let line = route?.polyline {
self.mapView.addOverlay(line, level: .aboveRoads)
}
}
}
//MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polyLine = overlay as? MKPolyline {
let lineRenderer = MKPolylineRenderer(polyline: polyLine)
lineRenderer.strokeColor = .red
lineRenderer.lineWidth = 3
return lineRenderer
}
return MKOverlayRenderer()
}
//MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
UserLocation.shared.location = locations.first
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedWhenInUse:
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()
case .denied:
UserLocation.shared.location = nil
locationManager.requestWhenInUseAuthorization()
case .notDetermined:
UserLocation.shared.location = nil
locationManager.requestWhenInUseAuthorization()
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Manager Error -> \(String(describing: error.localizedDescription))")
}
}
Add this class to hold the users location
class UserLocation {
static let shared = UserLocation()
var location: CLLocation?
}
In the Info.plist add this key and value
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location Usage Description Shown To The User</string>
I don't know if you added the two required strings into the plist project.
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription