Why the MKPolyline didn't show up in my application? - swift

I am trying to create an application, one of its function is to drawing the line while users are moving.
Here is the class
class traceuserViewController: UIViewController,CLLocationManagerDelegate, MKMapViewDelegate {
var locationManager = CLLocationManager()
var startLocation: CLLocation?
var endLocation: CLLocation?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.distanceFilter = 30.0
self.locationManager.startMonitoringSignificantLocationChanges()
self.locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
mapView.mapType = .hybrid
self.mapView.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//user's current location
let nowlocation = locations.last
userLocations.append(nowlocation!)
print("HERE IS THE LOCATION ARRAY")
print(userLocations)
//show the current location region
let center = CLLocationCoordinate2D(latitude: nowlocation!.coordinate.latitude, longitude: nowlocation!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.7, longitudeDelta: 0.7))
self.mapView.setRegion(region, animated: true)
drawRoute(locationArray: userLocations)
}
func drawRoute(locationArray: [CLLocation]) {
if (locationArray.count) > 1 {
var destinationLocIndex = (locationArray.count) - 1
var startLocIndex = (locationArray.count) - 2
let destinationloc = locationArray[destinationLocIndex].coordinate
let startLoc = locationArray[startLocIndex].coordinate
var routeArray = [startLoc, destinationloc]
//test if the function works well or not
print(routeArray)
var geodesicLine = MKGeodesicPolyline(coordinates: routeArray , count: routeArray.count)
mapView.add(geodesicLine, level: .aboveRoads)
}
}
//draw in the mapview
private func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer! {
if overlay is MKPolyline{
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.lineWidth = 5.0
return polylineRenderer
}else{
os_log("Failed to draw the polyline", log: OSLog.default, type: .debug)
return nil
}
}
After many times trying, I still have no idea why it doesn't draw the route on the map when the user is moving, can anyone please I've me some hints?
cheers

I'm inferring that you are using Swift 3 from the code snippet (e.g. the signature of didUpdateLocations; the use of .hybrid rather than Swift 2.3's .Hybrid; etc.).
But, the signature for mapView(_:rendererFor:) is incorrect. In Swift 3, it is:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
...
}
If you ever have a delegate method that doesn't appear to work, add a breakpoint in it and you can confirm if it's called at all or not (and if it is called, you can step through it and diagnose the problem further).

Related

MapView not displaying Route

I have already checked that coordinates are available, but when I launch the map snaps to Antartica, and the route is not drawn when I go to the place in the map it should be.
I am attempting to do this programatically so maybe there is something I missed, if you spot it please let me know. There is no additional code
import UIKit
import MapKit
class ViewController: UIViewController {
var locationManager : CLLocationManager?
var routeData : Route?
var routeCoordinates : [CLLocation] = []
var routeOverlay: MKOverlay?
let mapView : MKMapView = {
let map = MKMapView()
map.overrideUserInterfaceStyle = .dark
map.translatesAutoresizingMaskIntoConstraints = false
return map
}()
override func viewWillAppear(_ animated: Bool) {
setMapConstraints()
}
override func viewDidLoad() {
super.viewDidLoad()
if let routeJSON = self.getJSON() {
self.parseJSON(jsonData: routeJSON)
}
drawRoute(routeData: routeCoordinates)
print(routeCoordinates)
}
private func setMapConstraints() {
view.addSubview(mapView)
mapView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
mapView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}
//Omitted - functions for parsing local JSON file - tested and is working fine
//Function that handles drawing of route on map
func drawRoute(routeData: [CLLocation]) {
if routeCoordinates.count == 0 {
return
}
let coordinates = routeCoordinates.map { (location) -> CLLocationCoordinate2D in
return location.coordinate
}
DispatchQueue.main.async {
self.routeOverlay = MKPolyline(coordinates: coordinates, count: coordinates.count)
self.mapView.addOverlay(self.routeOverlay!, level: .aboveRoads)
let customEdgePadding: UIEdgeInsets = UIEdgeInsets(top: 40, left: 20, bottom: 40, right: 20)
self.mapView.setVisibleMapRect(self.routeOverlay!.boundingMapRect, edgePadding: customEdgePadding, animated: false)
}
}
}
extension ViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = .systemIndigo
renderer.lineCap = .round
renderer.lineWidth = 3.0
return renderer
}
}

Location manager show bad location in gmsMapView swift 4.2

I have a problem that a I cannot resolve. I'm trying to get the current location to update the mapview and center the camera, but when I close the view controller and init a new Mapviewcontroller the "current location" show a location incorrect, near to my position but not the current location. Can anyone guide me o help me? this is my code:
class MapContainerViewController: UIViewController{
#IBOutlet weak var mapView: GMSMapView!
var locationManager = CLLocationManager()
var myLocation: CLLocation?
var autorizeChange: Bool?
var geofire: GeoFire?
var createGeoforme: Bool?
var INITIAL_CENTER: CLLocation?
var searchCircle: GMSCircle?
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
autorizeChange = true
checkLocationServices()
createGeoforme = false
mapView.delegate = self
// Do any additional setup after loading the view.
}
func setupLocationManager(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 100
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startMonitoringSignificantLocationChanges()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func checkLocationServices(){
if CLLocationManager.locationServicesEnabled(){
setupLocationManager()
checkLocationAutorization()
} else {
}
}
func checkLocationAutorization(){
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
//Do map stuff
if (mapView.isMyLocationEnabled == false ){
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
var ref: DatabaseReference
ref = Database.database().reference()
geofire = GeoFire(firebaseRef: ref)
}
break
case .denied:
//Show alert instructing
break
case .notDetermined:
//location manager
locationManager.requestWhenInUseAuthorization()
break
case .restricted:
//Show alert letting them know
break
case .authorizedAlways:
break
default:
}
}
}
}
extension MapContainerViewController: GMSMapViewDelegate{
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition)
{
mapView.clear()
let coordinate = mapView.projection.coordinate(for: mapView.center)
var cooordinate2d = CLLocationCoordinate2D.init(latitude: coordinate.latitude, longitude: coordinate.longitude)
var circ = GMSCircle.init(position: coordinate, radius: zoomLevelToRadius(zoomLevel: Double(position.zoom)))
circ.fillColor = UIColor.init(displayP3Red: 0, green: 255, blue: 255, alpha: 0.15)
circ.strokeColor = UIColor.init(displayP3Red: 0, green: 0, blue: 0, alpha: 0.15)
circ.strokeWidth = 0.5
circ.map = mapView;
}
func zoomLevelToRadius(zoomLevel: Double) -> Double{
// Approximation to fit circle into view
return (16384000/pow(2, zoomLevel))
}
}
extension MapContainerViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let location = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
mapView.camera = GMSCameraPosition.camera(withTarget: location, zoom: 14)
let centerLocation = mapView.projection.coordinate(for: mapView.center)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAutorization()
}
}
This sample code can be your solution you can find here https://github.com/SwiftGuides/Google_Place_Picker
This is sample code i wrote for Google place picker to pick exact pin point custom location (not nearby location)
In your case you can apply this too. Just check the example code you will get you solution

How to Display User's Place on Snippet Google Maps Swift

I have google map and want to display user's place (like city) in snippet.
How to do that?
here's my current code:
class ViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate{
#IBOutlet weak var mapView: GMSMapView!
var latitude = -7.034323799999999
var longitude = 110.42400399999997
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mapView.delegate = self
let camera = GMSCameraPosition.camera(withLatitude: Double(latitude), longitude: Double(longitude), zoom: 17)
mapView.animate(to: camera)
let markerImage = UIImage(named: "ic_home_detail_marker_location")
let markerView = UIImageView(image: markerImage)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(Double(latitude), Double(longitude))
marker.isDraggable = true
marker.snippet = "\(marker.position)"
mapView.selectedMarker = marker
marker.iconView = markerView
mapView.selectedMarker = marker
marker.map = mapView
}
}
If you want to get the user's city or state name you have to use CLGeocoder.
var currentLatitude:Double!
var currentLongitude:Double!
var cityName:String!
var stateName:String!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager function called")
// Fetch current location coordinates
let locValue:CLLocationCoordinate2D = (locationManager.location?.coordinate)!
currentLatitude = locValue.latitude
currentLongitude = locValue.longitude
print("Current Location = \(currentLatitude!), \(currentLongitude!)")
// Zoom to current location
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: currentLatitude!, longitude: currentLongitude!, zoom: 17.0)
viewMap.camera = camera
// check for current city
CLGeocoder().reverseGeocodeLocation(locationManager.location!, completionHandler: {(placemarks, error) -> Void in
if error != nil {
print("Reverse geocoder failed with error" + (error?.localizedDescription)!)
return
}
if (placemarks?.count)! > 0 {
let pm = placemarks?[0]
self.cityName = (pm?.locality)!
self.stateName = (pm?.administrativeArea)
print("Current City: \(self.cityName!)")
print("Curret State: \(self.stateName!)")
}
else {
print("Problem with the data received from geocoder")
}
})
locationManager.stopUpdatingLocation()
}
Now you have the current city stored in a variable.
The next step is that when the user touches a marker, it should display the cityname. For this to achieve implement this:
This delegate must be added:
GMSMapViewDelegate
and this is the marker function, when user taps on it.
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
mapView.delegate = self
marker.snippet = ("Current city: \(cityName!)")
// return false so as to show the marker details or
// return true to hide marker details.
return false
}

Save map annotation using UserDefaults

I have been searching relentlessly for this solution and left with no choice except to rely on the expertise here on Stackoverflow. I am working on saving a map annotation if the app closes and I have been using the UserDefault to save the annotation.
This is the Objective C code that I found and I tried converting it into Swift and I think there is an error with it. I am not too sure. I place this code at viewDidLoad()
This is the save annotation
var pinnedAnnotation: CLLocationCoordinate2D = (parkedCarAnnotation?.coordinate)!
var coordinateData: NSData = NSData(bytesNoCopy: pinnedAnnotation, length: sizeof(pinnedAnnotation), freeWhenDone: false)
UserDefaults.standard.set(coordinateData, forKey: pinnedAnnotation)
UserDefaults.standard.synchronize()
And I needed a load annotation when the app open.
I dont know if viewDidLoad is the right place to put. Previously I put it in a mapView function of updatingLocation
Edited: Added code for further clarification of what I have done that needed to be corrected
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self as MKMapViewDelegate
checkLocationAuthorizationStatus()
tabBar.delegate = self
if UserDefaults.standard.object(forKey: "pinnedAnnotation") != nil {
let annotation = MKPointAnnotation()
self.mapView.addAnnotation(annotation)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let pinnedAnnotation: CLLocationCoordinate2D = (parkedCarAnnotation?.coordinate)!
let locationData = ["latitude": parkedCarAnnotation?.coordinate.latitude, "longitude": parkedCarAnnotation?.coordinate.longitude]
UserDefaults.standard.set(locationData, forKey: "pinnedAnnotation")
UserDefaults.standard.synchronize()
print("Saving data ", UserDefaults.standard.set(locationData, forKey: "pinnedAnnotation"))
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if(item.tag == 0){
if mapView.annotations.count == 1{
mapView.addAnnotation(parkedCarAnnotation!)
} else {
mapView.removeAnnotation(mapView.annotations as! MKAnnotation)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? ParkingSpot{
let identifier = "pin"
var view: MKPinAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.animatesDrop = true
view.pinTintColor = UIColor.orange
view.calloutOffset = CGPoint(x: -8, y: -3)
view.rightCalloutAccessoryView = UIButton.init(type:.detailDisclosure) as UIView
return view
} else {
return nil
}
}
extension ViewController: CLLocationManagerDelegate{
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
centerMapOnLocation(location: CLLocation(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude))
let locationServiceCoordinate = LocationService.instance.locationManager.location!.coordinate
parkedCarAnnotation = ParkingSpot(title: "My Parking Spot", locationName: "Find your way back to your car location", coordinate: CLLocationCoordinate2D(latitude: locationServiceCoordinate.latitude, longitude: locationServiceCoordinate.longitude))
}
}
I'm not entirely sure what you're asking...
For starters when you're saving data to UserDefaults the key needs to be a string, I also believe you'll need to save you data in UserDefaults as a Dictionary
let locationData = ["lat": parkedCarAnnotation?.coordinate?.latitude, "long": parkedCarAnnotation?.coordinate.longitude]
UserDefaults.standard.set(locationData, forKey: "pinned_annotation")
And then to retrieve the data you would call
if let annotationData = UserDefaults.standard.object(forKey: "pinned_annotation") as? Dictionary {
guard let lat = annotationData["lat"], let long = annotationData["long"] else { return }
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
Now you should hopefully be able to set your annotation with the coordinate

MKPolyLine not appearing on MapView in Swift

I have the following code in Swift to add an MKPolyline to a MapView. XCode isn't telling me there's an issue, and as far as I've read, this should be working.
Outlet for the MapView:
#IBOutlet weak var mapView: MKMapView!
Variable to hold the coordinates:
var coordinates: [CLLocationCoordinate2D] = []
Get my saved coordinates from Core Data:
var contextMap = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
var requestMap = NSFetchRequest(entityName: "Locations")
let predMap = NSPredicate(format: "game = %d", passedGameNumber)
requestMap.predicate = predMap
requestMap.sortDescriptors = [NSSortDescriptor(key:"time", ascending: false)]
self.locationsList = contextMap.executeFetchRequest(requestMap, error: nil)! as [Locations]
Add the coordinates from Core Data to my new array:
for index in 1..<self.locationsList.count{
var lat = Double(self.locationsList[index].latitude)
var long = Double(self.locationsList[index].longitude)
var coordinatesToAppend = CLLocationCoordinate2D(latitude: lat, longitude: long)
coordinates.append(coordinatesToAppend)
}
Create the polyline:
var polyLine = MKPolyline(coordinates: &coordinates, count: coordinates.count)
Add the overlay:
self.mapView.addOverlay(polyLine, level: MKOverlayLevel.AboveRoads)
Add it to the MapView:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay.isKindOfClass(MKPolyline) {
// draw the track
let polyLine = overlay
let polyLineRenderer = MKPolylineRenderer(overlay: polyLine)
polyLineRenderer.strokeColor = UIColor.blueColor()
polyLineRenderer.lineWidth = 2.0
return polyLineRenderer
}
return nil
}
I simply get a blank MapView. I can print the coordinates array to the console, so I know the data has been added. Any ideas?
The above method will be written like this in Swift3:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay.isKind(of: MKPolyline.self) {
// draw the track
let polyLine = overlay
let polyLineRenderer = MKPolylineRenderer(overlay: polyLine)
polyLineRenderer.strokeColor = UIColor.blue
polyLineRenderer.lineWidth = 2.0
return polyLineRenderer
}
return MKPolylineRenderer()
}
As posted in the comments, the code in the question was fine. I simply wasn't setting the delegate.
mapView.delegate = self