1

Basically I want to have an arrow image that rotates in order to direct the user to a nearby location. i'm using core location heading data to get direction information, the code for this is bellow.

 func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
    print("MAGNETIC HEADING: \(newHeading.magneticHeading)")
    print("TRUE HEADING  \(newHeading.trueHeading)")

    var degrees = newHeading.trueHeading
    //Rotate the arrow image
    if self.arrowImageView != nil {

        self.arrowImageView.transform = CGAffineTransform(rotationAngle: CGFloat(degrees * (180.0 / M_PI)))
    }


}

I'm not sure if this is the correct approach or not.

2 Answers2

1

I managed to solve this using the C# solution found here How to rotate a Direction Arrow to particular location

func UpdateCompass(origin: CLLocationCoordinate2D,target:CLLocationCoordinate2D, heading: CLHeading)
{
    var angle1 = GetAngleBetweenPoints(origin: origin, target: target);
    var angle2 = GetAngleFromHeading(heading: heading);
    var radian = .pi * (angle1 + angle2) / 180.0;
    let res =  radiansToDegrees(radians:radian)
    self.arrowImageView.transform = CGAffineTransform(rotationAngle: CGFloat(radian))
    //self.CompassImageView.transform = CGAffineTransform(rotationAngle: CGFloat(radian))
    UIView.animate(withDuration: 0.5, animations: {
       self.CompassImageView.transform = CGAffineTransform(rotationAngle: CGFloat(radian))
    })
}

func GetAngleBetweenPoints(origin:CLLocationCoordinate2D,target: CLLocationCoordinate2D) -> Double
{
var n = 270 - (atan2(origin.latitude - target.latitude, origin.longitude - target.longitude)) * 180 / .pi;
return n.truncatingRemainder(dividingBy: 360);
}

func GetAngleFromHeading(heading:CLHeading) -> Double
{
var radians = -heading.magneticHeading / 180.0 * .pi;
return radians * (180.0 / .pi);
}
0

Well, one big concern is on what thread you started locationManager. It calls its delegate methods on that same thread. Now you probably started it on the main thread but I don’t know that from looking at this code. And of course, you would not want to be transforming a UIView on anything but the main thread (main queue, technically).

Another concern is I’d separate the rotation code from the locationManager code. You want to be able to test them independently, and there are possibly times when you may want to force the heading to a particular value.

Another thought is performance. How many times a second can the heading delegate be called? If sometimes more than 10, you should skip those - the human eye won’t discern more motion than that. In fact for this purpose I’d cap it at 1s.

You should also consider checking the headingAccuracy. Do you want to accept and display even the most inaccurate “guesses” of heading?

As for rotating the view, you may run into layout constraint issues if autolayout matters to you for this view. See this SO for details. I do wonder if you can rotate a layer of your view instead.

Another thing to worry about is how your view behaves during and after device orientation changes. This SO code, if changed for your goals, would be useful if device orientation proves to be a problem.

All in all, I’d choose changing the view’s image (by drawing into the view) or layer over rotating the entire view, to avoid some of the above concerns. I bet it would be more performant too.

Smartcat
  • 2,834
  • 1
  • 13
  • 25
  • Thanks for the pointers, I'm rotating an image view which is placed inside a UIViewcontroller. The size of the imageView it's self is small, less then 70pt. All of the location code is set up in viewDidLoad along with the map view settings. the delegate methods for these are split into separate extension within the class. hope this gives you more insight and clarificaton. – William Titterton Mar 09 '18 at 14:27