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
}
Related
I am trying to draw a path behind the user as they move, tracking their path (like Strava or FitBit apps do when a user starts a workout). So far, the map centres on the user's location but does not start drawing when the user moves. I have tried to implement this with renderForOverlay, but it fails to do so when tested. The code is as follows:
ViewController.swift
import UIKit
import MapKit
import CoreLocation
class StartWorkoutViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapsView: MKMapView!
#IBOutlet weak var startButton: UIButton!
var locationManager: CLLocationManager!
var allLocations: [CLLocation] = []
#IBAction func startButton(_ sender: Any) {
// Start the workout
}
override func viewDidLoad() {
super.viewDidLoad()
// Request user's current location
locationManager = CLLocationManager()
locationManager?.requestAlwaysAuthorization()
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager?.startUpdatingLocation()
locationManager?.startUpdatingHeading()
locationManager?.delegate = self
mapsView?.showsUserLocation = true
mapsView?.mapType = MKMapType(rawValue: 0)!
mapsView?.userTrackingMode = .follow
mapsView?.delegate = self
let noLocation = CLLocationCoordinate2D()
let viewRegion = MKCoordinateRegion(center: noLocation, latitudinalMeters: 100, longitudinalMeters: 100)
mapsView?.setRegion(viewRegion, animated: true)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Add location to the array and prepare to draw a line between last location and current location
print("Location Updated")
allLocations.append(locations[0])
let previousLocation = allLocations[allLocations.count - 1]
let newLocation = locations[0]
let previousCoordinates = previousLocation.coordinate
let newCoordinates = newLocation.coordinate
var area = [previousCoordinates, newCoordinates]
let polyline = MKPolyline(coordinates: &area, count: area.count)
mapsView.addOverlay(polyline)
}
// DOES NOT WORK
func mapView(_ mapsView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.red
polylineRenderer.lineWidth = 4
return polylineRenderer
} else {
return MKPolylineRenderer()
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// If the authorisation for the user's location has changed, ask again
if status != .authorizedAlways {
locationManager = CLLocationManager()
locationManager?.requestAlwaysAuthorization()
}
}
}
Thank you!
The problem is that you’re grabbing a location, adding it to the array, and then creating a polyline from the last location in the array, allLocations[allLocations.count - 1], (which is now the current location) to the current location (i.e. to itself).
So, grab the last item, previousCoordinate, from the array before you add the new location to it:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.first(where: { $0.horizontalAccuracy >= 0 }) else {
return
}
let previousCoordinate = allLocations.last?.coordinate
allLocations.append(currentLocation)
if previousCoordinate == nil { return }
var area = [previousCoordinate!, currentLocation.coordinate]
let polyline = MKPolyline(coordinates: &area, count: area.count)
mapsView.addOverlay(polyline)
}
I'd also suggest, as you see above, checking for the horizontal accuracy of the location update, to make sure it’s non-negative.
Anyway, that yields:
A few other observations:
I'd suggest retiring the noLocation pattern in viewDidLoad. My above pattern doesn't require that dummy value in the array.
Another issue is that in didChangeAuthorization, you are instantiating a new CLLocationManager and not setting its properties. You are therefore losing the configuration of the original CLLocationManager in viewDidLoad. There’s no need to instantiate another one, but if you do, remember to configure it properly.
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! :)
So basically, I'm calling a Rest API to get all Bus Stops location, then put annotation of all bus stops within 5km from my current location on the map when a button is called. However, it is just not displaying, I can't seem to figure out the problem.
import UIKit
import MapKit
class MapKitViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var GPSButton: UIButton!
var stopSearchResults: [Value] = []
var Annotations: [BusStopAnnotation] = []
let queryServices = QueryService()
let locationManager:CLLocationManager = CLLocationManager()
#IBOutlet weak var mapView: MKMapView!
var currentLocation: CLLocationCoordinate2D?
var counter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.isNetworkActivityIndicatorVisible = true
queryServices.GetAllBusStops(){
result in
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if let result = result {
self.stopSearchResults = result.value
}
}
configureLocationService()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func configureLocationService() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestAlwaysAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdate(locationManager: locationManager)
}
}
private func beginLocationUpdate(locationManager: CLLocationManager) {
mapView.showsUserLocation = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
private func zoomToLatestLocation(with coordinate: CLLocationCoordinate2D) {
let zoomRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
mapView.setRegion(zoomRegion, animated: true)
}
#IBAction func GPSTrack(_ sender: Any) {
InputAllAnnotation(busStops: stopSearchResults)
print("Searching for nearby bus stops")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest location")
guard let latestLocation = locations.first else { return }
if currentLocation == nil {
zoomToLatestLocation(with: latestLocation.coordinate)
}
currentLocation = latestLocation.coordinate
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("The status changed")
if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdate(locationManager: manager)
}
}
func InputAllAnnotation(busStops: [Value]) {
for busStop in busStops{
let busStopObj = BusStopAnnotation(value: busStop)
Annotations.append(busStopObj)
let distance = busStop.GetDistance(latitude: Double(currentLocation?.latitude ?? 0), longitude: Double(currentLocation?.longitude ?? 0))
if distance < 5000 {
mapView.addAnnotation(busStopObj)
}
}
}
}
extension MapKitViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let busStopAnnotation = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) as?
MKMarkerAnnotationView {
busStopAnnotation.animatesWhenAdded = true
busStopAnnotation.titleVisibility = .adaptive
busStopAnnotation.canShowCallout = true
return busStopAnnotation
}
return nil
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("The annotation was selected: \(String(describing: view.annotation?.title))")
}
}
final class BusStopAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var busStopCode: String?
init(value : Value) {
self.coordinate = value.GetLocationCoordinate2D()
self.title = value.roadName
self.subtitle = value.description
self.busStopCode = value.busStopCode
}
init(coordinate: CLLocationCoordinate2D, roadName: String?, description: String?, busStopCode: String?) {
self.coordinate = coordinate
self.title = roadName
self.subtitle = description
self.busStopCode = busStopCode
}
var region: MKCoordinateRegion {
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
return MKCoordinateRegion(center: coordinate, span: span)
}
}
You may need
self.mapView.delegate = self
import:
import UIKit
import MapKit
set class
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
outlet your map
#IBOutlet weak var map: MKMapView!
Code:
let customPin : CLLocationCoordinate2D = CLLocationCoordinate2DMake(Latitude, Longitude)
let objectAnnotation = MKPointAnnotation()
objectAnnotation.coordinate = customPin
objectAnnotation.title = "Here's your custom PIN"
self.map.addAnnotation(objectAnnotation)
extra:
to set the camera near the PIN
let theSpan:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.009, longitudeDelta: 0.009)
let pointLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(Latitude, Longitude)
let region:MKCoordinateRegion = MKCoordinateRegion(center: pointLocation, span: theSpan)
self.map.setRegion(region, animated: true)
move values depending how close/far you want the camera
let theSpan:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: HERE, longitudeDelta: HERE)
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 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