Suthan M
Suthan M

Reputation: 443

Rotate AnnotationView in particular Coordinates using Swift

I created the apple map application and handled location mapping using MKCircle and MKPolyline.

And I used

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? 
{}

above method for Custom annotation update in the map view.

Thing is can i rotate the image while the annotation reaching particular point of time.

My requirement moving the annotation by three locations started from Los angeles and New York finally Mexico City, So while the annotation reaching New York I want to rotate the annotation image. Is it possible please suggest me some note.

For more info please find the below coding part.

    //
    //  ViewController.swift
    //  RouteMap
    //
    //  Created by Suthan M.
    //  Copyright © 2016 suthan.nimalan.m All rights reserved.
    //

    import UIKit
    import MapKit

    extension Double {
    /** Converts the specified value in degrees into radians. */

    func degrees() -> CGFloat {
        return CGFloat(self) * CGFloat(M_PI / 180.0)
    }
}

class ViewController: UIViewController,MKMapViewDelegate {

    @IBOutlet var mkMapView: MKMapView!

    var flightpathPolyline: MKGeodesicPolyline!
    var planeAnnotation: MKPointAnnotation!
    var planeAnnotationPosition = 0
    var planeDirection: CLLocationDirection!
    var testValue: MKMapPoint!
    var testCount: Int!
    var radiusInSomething : CLLocationDistance = 10000000.0
    var circle:MKCircle!

    override func viewDidAppear(animated: Bool) {

        let location = CLLocationCoordinate2D(
            latitude: 33.9424955,
            longitude: -118.4080684
        )

        self.mkMapView.setRegion(MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 95, longitudeDelta: 95)), animated: true)

        let LAX = CLLocation(latitude: 33.9424955, longitude: -118.4080684)
        let JFK = CLLocation(latitude: 40.6397511, longitude: -73.7789256)
        let WAS = CLLocation(latitude: 19.432608, longitude: -99.133209)
        let mexicoCityLoc = CLLocationCoordinate2D(latitude: 19.4284700, longitude: -99.1276600)

        mkMapView.addOverlay(MKCircle(centerCoordinate: mexicoCityLoc,
            radius: radiusInSomething * MKMetersPerMapPointAtLatitude(mexicoCityLoc.latitude)))

        var coordinates = [LAX.coordinate, JFK.coordinate, WAS.coordinate]
        flightpathPolyline = MKGeodesicPolyline(coordinates: &coordinates, count: 3)

        mkMapView.addOverlay(flightpathPolyline)

        let coordin = CLLocationCoordinate2D(latitude: 19.4284700, longitude: -99.1276600)
        circle = MKCircle(centerCoordinate: coordin, radius: 2000)
        mkMapView.addOverlay(circle)

        let annotation = MKPointAnnotation()
        annotation.title = NSLocalizedString("Plane", comment: "Plane marker")
        mkMapView.addAnnotation(annotation)

        self.planeAnnotation = annotation
        self.updatePlanePosition()

    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // Work with MKMapview

        self.planeDirection = 360.0
        testCount = 0

        mkMapView.delegate = self
        mkMapView.mapType = MKMapType.Standard
        mkMapView.showsUserLocation = true

    }


    func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {


        if overlay is MKPolyline {

            let renderer = MKPolylineRenderer(overlay: overlay)
            renderer.lineWidth = 3.0
            renderer.alpha = 0.5
            renderer.strokeColor = UIColor.blueColor()

            return renderer

        }else if overlay is MKCircle {

            let circleRenderer = MKCircleRenderer(overlay: overlay)
            circleRenderer.fillColor = UIColor.blueColor().colorWithAlphaComponent(0.1)
            circleRenderer.strokeColor = UIColor.blueColor()
            circleRenderer.lineWidth = 1

            return circleRenderer

        }

        return MKOverlayRenderer()
    }

    func updatePlanePosition() {

        let step = 5
        guard planeAnnotationPosition + step < flightpathPolyline.pointCount
            else { return }

        let points = flightpathPolyline.points()
        self.planeAnnotationPosition += step
        let nextMapPoint = points[planeAnnotationPosition]

        self.planeAnnotation.coordinate = MKCoordinateForMapPoint(nextMapPoint)

        let previousMapPoint = points[planeAnnotationPosition]
        planeAnnotationPosition += step

        self.planeDirection = directionBetweenPoints(previousMapPoint, nextMapPoint)

        // set turn point of plane

        if(testValue.x == 79197107.0566392){

            testCount = 1
            planeDirection = 360.0

            self .mapView(mkMapView, viewForAnnotation: planeAnnotation )
        }

        performSelector("updatePlanePosition", withObject: nil, afterDelay: 0.03)

    }

    private func directionBetweenPoints(sourcePoint: MKMapPoint, _ destinationPoint: MKMapPoint) -> CLLocationDirection {

        //print(sourcePoint)

        //MKMapPoint(x: 74074213.8054451, y: 105154930.159039)
        //MKMapPoint(x: 74074213.8054451, y: 105154930.159039)

        testValue = sourcePoint

        let x = destinationPoint.x - sourcePoint.x
        let y = destinationPoint.y - sourcePoint.y

        return radiansToDegrees(atan2(y, x)) % 360 + 90
    }

    private func radiansToDegrees(radians: Double) -> Double {
        return radians * 180 / M_PI
    }

    private func degreesToRadians(degrees: Double) -> Double {
        return degrees * M_PI / 180
    }

    func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

        let planeIdentifier = "Plane"

        let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(planeIdentifier)
            ?? MKAnnotationView(annotation: annotation, reuseIdentifier: planeIdentifier)

        annotationView.image = UIImage(named: "pointer")

        if(testCount == 1){

            let angle = CGFloat(self.degreesToRadians(self.planeDirection))
            annotationView.transform = CGAffineTransformRotate(mapView.transform, angle)

        }
        else{

            let angle = CGFloat(degreesToRadians(planeDirection))
            annotationView.transform = CGAffineTransformRotate(mapView.transform, angle)

        }

        return annotationView

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

Current Snap

Thanks.

Upvotes: 3

Views: 1822

Answers (2)

J Kasparian
J Kasparian

Reputation: 537

What I do in this case is delete the annotation and re-add it to the map with the updated image. Something like that:

    DispatchQueue.main.async {
        self.mkMapView.removeAnnotation(planeAnnotation)
        updatedAnnotation()
        self.mkMapview.addAnnotation(planeAnnotation)
    }

If you have several annotations and want to update only some of them, you can loop on all annotations, applying some relevant filtering. Eg:

    DispatchQueue.main.async {
        for annotation in self.mkMapView.annotations.filter({ [insert the relevant filter here, eg $0 is PlaneAnnotation] }) {
            self.mkMapView.removeAnnotation(annotation)
            updatedAnnotation(annotation)  // or any other routine updating the annotation image
            self.mkMapview.addAnnotation(annotation)
        }
    }

Upvotes: 0

Subin K Kuriakose
Subin K Kuriakose

Reputation: 849

In my case i got the heading (angle) to the destination . So i did it in my project is like rorate the image not the annotation view.

//viewForAnnotation method

 annotationView.image = UIImage(named: "pointer")?.imageRotatedByDegrees(CGFloat(angle), flip: false)

I added a extension .https://ruigomes.me/blog/how-to-rotate-an-uiimage-using-swift/

 extension UIImage {
      public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool)   -> UIImage {
      //        let radiansToDegrees: (CGFloat) -> CGFloat = {
     //         return $0 * (180.0 / CGFloat(M_PI))
     //     }

    let degreesToRadians: (CGFloat) -> CGFloat = {
        return $0 / 180.0 * CGFloat(M_PI)
    }

    // calculate the size of the rotated view's containing box for our drawing space
    let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
    let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
    rotatedViewBox.transform = t
    let rotatedSize = rotatedViewBox.frame.size

    // Create the bitmap context
    UIGraphicsBeginImageContext(rotatedSize)
    let bitmap = UIGraphicsGetCurrentContext()

    // Move the origin to the middle of the image so we will rotate and scale around the center.
    CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0, rotatedSize.height / 2.0);

    // // Rotate the image context
    CGContextRotateCTM(bitmap, degreesToRadians(degrees));

    // Now, draw the rotated/scaled image into the context
    var yFlip: CGFloat

    if (flip) {
        yFlip = CGFloat(-1.0)
    } else {
        yFlip = CGFloat(1.0)
    }

    CGContextScaleCTM(bitmap, yFlip, -1.0)
    CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
   }
  }

Upvotes: 1

Related Questions