3

What I want to achieve is like the animation (how the map rotates) in mapView when userTrackingMode is followWithHeading.

The thing is I can't use this tracking mode here because we draw our own blue dot based on the different location information instead of the one provided by iOS. Every time the heading get updated, I want to set the mapView's camera heading value, but animated to make it smooth.

Currently I've tried:

  1. setCamera: animated: This doesn't work well when the heading changes frequently.
  2. animateWithDuration:animations:completion: This doesn't work at all.
  3. I tried CADisplayLink like the code below, still the transition is not smooth.

CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateHeading)]; timer.frameInterval = 5; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];

and in the updateHeading method I just set the mapView's camera to the current heading self.camera.heading = newHeading.trueHeading;

I guess this doesn't work because I am only setting the camera every 5/60 seconds here, it's not animating.

Can someone point out the correct way to achieve this? Any thought will be appreciated.

Robert
  • 67
  • 2
  • 6
  • I'm facing the same probleme, did you find any solutions ? thanks in advance – TheFuquan Sep 28 '17 at 11:12
  • 1
    We end up using the setCamera: animated: method. The transition is somehow smooth at the end. I think it depends on the how often you get the location update. – Robert Sep 28 '17 at 16:40

1 Answers1

0

I've had some luck using CADisplayLink, if you move a larger amount like 170 degrees it is a bit slower but in general it works smoothly. I'm going to look at adding an easing function to it but for the time being it's the smoothest route I've seen.

private var headingDisplayLink: CADisplayLink?
private var desiredHeading: Double = 0

func locationManager(didUpdateHeading newHeading: CLHeading) {
    self.updateHeading(to: newHeading.trueHeading)
}

private func updateHeading(to heading: Double) {
    self.desiredHeading = heading.rounded()

    if self.headingDisplayLink == nil {
        self.headingDisplayLink = CADisplayLink(target: self, selector: #selector(self.headingDisplayLinkUpdate(link:)))
        self.headingDisplayLinkUpdate(link: self.headingDisplayLink!)
        self.headingDisplayLink?.add(to: RunLoop.main, forMode: .defaultRunLoopMode)
    }
}

@objc private func headingDisplayLinkUpdate(link: CADisplayLink) {
    if let camera = self.mapView?.camera.copy() as? MKMapCamera {
        var delta: Double = self.desiredHeading > camera.heading ? 1 : -1
        let difference = fabs(camera.heading - self.desiredHeading)
        if difference > 180 {
            delta *= -1
        }

        camera.heading += delta
        self.mapView?.setCamera(camera, animated: false)

        if camera.heading.rounded() == self.desiredHeading {
            self.headingDisplayLink?.invalidate()
            self.headingDisplayLink = nil
        }
    }
}
Allan Weir
  • 628
  • 5
  • 12