I have a live tracking application, where I am tracking vehicles. I am getting vehicle position in every 10 seconds and moving vehicle car icon on map with animation like Uber, Ola.
My animation is running good but problem is on car MKAnnotation heading. How to change heading of car when every time my car position is changing?
I am using apple mapkit with swift and not having good knowledge of ObjectiveC.
MyViewConroller code is here
import UIKit
import Alamofire
import SwiftyJSON
import MapKit
import SwiftLoader
class LiveTrackingVC: UIViewController , MKMapViewDelegate{
//Outlets
@IBOutlet weak var deviceNameLAble: UILabel!
@IBOutlet weak var myNavigation: UINavigationItem!
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var lbAddress: UILabel!
@IBOutlet weak var lbDistance: UILabel!
@IBOutlet weak var lbAssestSpeed: UILabel!
// Classes Objects
let session = XSession()
var assestID = String().self
var lastPosition : CLLocationCoordinate2D?
var livePosition : CLLocationCoordinate2D?
var pointAnnotation : CarCustomeAnnotation!
var pinAnnotationView : MKAnnotationView!
let reuseIdentifier = "pin"
var timer = Timer()
@IBAction func finish(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
self.timer.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
pointAnnotation = CarCustomeAnnotation()
liveAPI()
timer.invalidate()
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(liveAPI), userInfo: nil, repeats: true)
self.mapView.isRotateEnabled = false
}
/*
Get live data from server
with assest ID.
*/
@objc func liveAPI() {
// print(self.session.getData(XConstant.webURL)+"/livestatus_new")
MyCall().call(WithRequest: XSApi.liveTracking(assestID), AndSuccessHandler: {
response in
SwiftLoader.hide()
let code = response?[API.SUCCESS].intValue
if(code == API.OK)
{
// Here is getting vehcile latest location data
self.handleLiveApi(listData: response![API.DATA].dictionaryValue)
}
})
{(response, error) in
print("Error")
}
}
/*
Live Tracking respone Handle
*/
func handleLiveApi(listData : Dictionary<String, JSON>)
{
self.lbDistance.text = listData["mileAge"]?.stringValue
let time = "".convertDateFormatter(date: (listData["DeviceTime"]?.stringValue)! )
self.myNavigation.title = time
self.deviceNameLAble.text = listData["DisplayName"]?.stringValue
let speed = (listData["Speed"]?.stringValue)!
self.lbAssestSpeed.text = speed + " km/h"
livePosition = CLLocationCoordinate2DMake(Double((listData["Latitude"]?.stringValue)!)!, Double((listData["Longitude"]?.stringValue)!)!)
pointAnnotation.pinCustomImageName = "ic_car_map"
if(lastPosition != nil )
{
pointAnnotation.courseDegrees = Double((lastPosition?.bearingToLocationDegrees(destinationLocation: livePosition!))!)
}
pinAnnotationView = MKAnnotationView(annotation: pointAnnotation, reuseIdentifier: reuseIdentifier)
mapView.addAnnotation(pinAnnotationView.annotation!)
if( lastPosition != nil)
{
pinAnnotationView.transform = CGAffineTransform(rotationAngle: CGFloat(pointAnnotation.courseDegrees))
self.moveCar(self.livePosition!)
}else{
pointAnnotation.coordinate = livePosition!
self.zoomMap(lat: (livePosition?.latitude)!,lng: (livePosition?.longitude)!)
}
lastPosition = livePosition
}
func zoomMap(lat:Double,lng:Double) {
let location = CLLocation(latitude: lat, longitude: lng)
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
1500 * 2.0, 1500 * 2.0)
self.mapView.setRegion(coordinateRegion, animated: true)
}
//--------------------------------------------------
//MARK:
//MARK: - Custom Methods
//--------------------------------------------------
func degreesToRadians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
func radiansToDegrees(radians: Double) -> Double { return radians * 180.0 / .pi }
func getHeadingForDirectionFromCoordinate (_ fromLoc : CLLocationCoordinate2D , toLoc : CLLocationCoordinate2D) -> Double {
let fLat = degreesToRadians(degrees: fromLoc.latitude)
let fLng = degreesToRadians(degrees: fromLoc.longitude)
let tLat = degreesToRadians(degrees: toLoc.latitude)
let tLng = degreesToRadians(degrees: toLoc.latitude)
let degree = radiansToDegrees(radians: atan2(sin(tLng-fLng) * cos(tLat), cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(tLng-fLng)))
if (degree >= 0) {
return degree
} else {
return 360.0 + degree
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) ->
MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotationView?.annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = false
} else {
annotationView?.annotation = annotation
annotationView?.canShowCallout = false
}
annotationView?.image = UIImage.init(named:pointAnnotation.pinCustomImageName)
return annotationView
}
//Inert Animation Duration and Destination Coordinate which you are getting from server.
func moveCar(_ destinationCoordinate : CLLocationCoordinate2D) {
UIView.animate(withDuration: 10, animations: {
self.pointAnnotation.coordinate = destinationCoordinate
}, completion: nil) }
}
public extension CLLocationCoordinate2D {
func bearingToLocationRadian(_ destinationLocation: CLLocationCoordinate2D) -> CGFloat {
let lat1 = self.latitude.degreesToRadians
let lon1 = self.longitude.degreesToRadians
let lat2 = destinationLocation.latitude.degreesToRadians
let lon2 = destinationLocation.longitude.degreesToRadians
let dLon = lon2 - lon1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
var radiansBearing = atan2(y, x)
if(radiansBearing < 0.0)
{
radiansBearing += 2*Double.pi;
}
return CGFloat(radiansBearing)
}
func bearingToLocationDegrees(destinationLocation: CLLocationCoordinate2D) -> CGFloat {
return CGFloat(Double(bearingToLocationRadian(destinationLocation)).radiansToDegrees)
}
}
CarCustomeAnnotation
import UIKit import MapKit
class CarCustomeAnnotation: MKPointAnnotation
{
var pinCustomImageName:String!
var courseDegrees : Double! // Change The Value for Rotating Car Image Position
}