iosswiftgoogle-mapsgmsmapviewcatransaction

Swift - Movement of GMSMarker along array of CLCoordinates from GMSPath (Google Maps SDK for iOS)


All attempts at animating marker movement in Google Maps between coordinates point to using the following snippet of code in Swift:

CATransaction.begin()
CATransaction.setAnimationDuration(duration)
marker.position = coordindates
CATransaction.commit()

As an example here is the SO post with most votes: How to smoothly move GMSMarker along coordinates in Objective c

This works ok when animating between an origin and destination pair of coordinates. However, I'm looking to animate from the starting coordinate in a GMSPath to the ending coordinate. When iterating through the points in the path, the only displayed animation is between the last two coordinates. The marker only appears at the second to last point and animates to the final point.

Here is my code for a ViewController. It is receiving a segment of a route as an encoded path (for testing, first encoded path: "ika~Exi|vN|AaDzAyCTc@N[lBeEvB_ExBkExBmEjBwDXo@").

The code is iterating through all stored coordinates inside the GMSPath object and attempting to animate using the snippet posted above. As stated, it only shows animation between the last two points.

I have tried placing all sets of code in ViewDidLoad, ViewDidAppear, and ViewWillAppear. ViewDidLoad kept the zoom level at intercontinental. ViewDidAppear and ViewWillAppear brought the zoom in appropriately, and resulted in the issues around animation mentioned in this post. The code is currently split between ViewDidAppear and ViewWillAppear, but will act the same if placed solely in either method.

import UIKit
import GoogleMaps
import CoreLocation

class MapVC:UIViewController {

    var mapView:GMSMapView?

    var polyline:GMSPolyline?

    var path:GMSPath?

    var encodedPath:String? = nil

    var marker:GMSMarker?

    override func viewDidLoad() {
        super.viewDidLoad()


        setupMap()

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if encodedPath != nil {

            self.path = GMSPath(fromEncodedPath: encodedPath!)

            self.polyline = GMSPolyline(path: path)
            self.polyline!.map = self.mapView!

            let bounds:GMSCoordinateBounds = GMSCoordinateBounds(path: path!)

            let update = GMSCameraUpdate.fit(bounds, withPadding: 10.0)
            self.mapView!.animate(with: update)



        } else {

            print("nil path")

        }
        let a=2

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        var index:UInt = 0

        let count:UInt = self.path!.count()

        if count > 0 {

            marker = GMSMarker(position: self.path!.coordinate(at:index))
            marker!.map = self.mapView

            index += 1

            while index < count {

                CATransaction.begin()
                CATransaction.setAnimationDuration(30)
                self.marker!.position = self.path!.coordinate(at:index)
                CATransaction.commit()
                index += 1

            }

        }

    }




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

    func setupMap() {

        let camera = GMSCameraPosition.camera(withLatitude: 36.5, longitude: -82.5, zoom: 16)
        mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

        self.view = mapView

    }

}

Solution

  • Found a solution using a timer: https://github.com/antonyraphel/ARCarMovement/blob/master/ARCarMovementSwift/ARCarMovementSwift/ViewController.swift

    Updated code from the view controller shown above:

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
    
            marker = GMSMarker(position: self.path!.coordinate(at:self.index))
            marker!.map = self.mapView
    
            timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: 
                    #selector(MapVC.timerTriggered), userInfo: nil, repeats: true)
    
    
        }
    
    
        @objc func timerTriggered() {
    
            if self.index < self.path!.count() {
    
                CATransaction.begin()
                CATransaction.setAnimationDuration(1.9)
                self.marker!.position = self.path!.coordinate(at:index)
                CATransaction.commit()
                self.index += 1
    
            } else {
    
                timer.invalidate()
                timer = nil
    
            }
    
        }