How to animate the new annotation in iOS 17 in MapKit based on a change in location?

114 Views Asked by At

In MK this was strait forward, but I can't seem to find anything that works with the new MapKit.

Requirement: When location changes, animate smoothly the annotation from oldValue to newValue.

So I have tried almost everything, but eventually landed on the notion of fractionalizing the delta between oldValue and newValue by 100, then brute force updating the position for each fraction. Here is the function I am working with:

func animateAnnotationMove(from oldCoordinate: CLLocationCoordinate2D, to newCoordinate: CLLocationCoordinate2D, steps: Int = 100, interval: TimeInterval = 0.05) {
    let latitudeDiff = (newCoordinate.latitude - oldCoordinate.latitude) / Double(steps)
    let longitudeDiff = (newCoordinate.longitude - oldCoordinate.longitude) / Double(steps)
    
    var currentStep = 0
    Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] timer in
        guard currentStep < steps else {
            timer.invalidate() // Stop the timer
            return
        }
        
        let nextLatitude = oldCoordinate.latitude + (latitudeDiff * Double(currentStep))
        let nextLongitude = oldCoordinate.longitude + (longitudeDiff * Double(currentStep))
        let nextCoordinate = CLLocaionCoordinate2D(latitude: nextLatitude, longitude: nextLongitude)
        
        DispatchQueue.main.async {
            self?.updateGuestAnnotationPosition(with: nextCoordinate)
        }
        
        currentStep += 1
    }
}

My concern is that this is not very efficient as I scale, and with SwiftUI's ability to just animate in many other areas, maybe I'm missing something obvious? My location data updates every 5 seconds, so the thinking here is to break that down into 100 function calls (so every 0.05 seconds) and animate the delta.

1

There are 1 best solutions below

0
bal simpson On

This is how I got my custom annotation to animate including the Map keeping the annotation in the center.

@State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)))

var body: some View {
        
        @State var location: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: plane.lat ?? 0, longitude: plane.lon ?? 0)
        
        @State var region: MKCoordinateRegion = MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.3, longitudeDelta: 0.3))
        
        Map(
            position: $cameraPosition,
            //initialPosition: .region(region)
            interactionModes: [.pan, .zoom]
        ) {
            
            Annotation(coordinate: location) {
                
                Image(systemName: "airplane")
                    .rotationEffect(Angle(degrees: (plane.track ?? 0) + -90))
                    .animation(.easeInOut(duration: 1.5), value: plane.track)
                
            } label: {
                Label {
                    VStack(alignment: .leading) {
                        Text(plane.desc ?? "")
                    }
                } icon: {
                    Image(systemName: "airplane")
                }
            }
        }
        .onChange(of: plane.lon) {
            withAnimation(.smooth(duration:  3.5)) {
                location = CLLocationCoordinate2D(latitude: plane.lat ??  0, longitude: plane.lon ??  0)
                region = MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta:  0.3, longitudeDelta:  0.3))
                cameraPosition = .region(region)
            }
        }
        .animation(.easeInOut(duration: 0.5), value: location)
        .mapControlVisibility(.visible)
        .ignoresSafeArea()
    }