Как получить анимированный полилинейный маршрут в GMSMapView, чтобы он двигался вместе с картой при перемещении карты?

Я создал анимированную полилинию, такую ​​​​как CAShapeLayer, с помощью следующего кода, я добавил CAShapeLayer в качестве подслоя к GMSMapiew, но если я перемещу карту, слой не сдвинется. куда добавить слой, чтобы он двигался вместе с картой?

   func layer(from path: GMSPath) -> CAShapeLayer {
        let breizerPath = UIBezierPath()
        let firstCoordinate: CLLocationCoordinate2D = path.coordinate(at: 0)
        breizerPath.move(to: self.mapView.projection.point(for: firstCoordinate))
        for i in 1 ..< Int((path.count())){
            print(path.coordinate(at: UInt(i)))
            let coordinate: CLLocationCoordinate2D = path.coordinate(at: UInt(i))
            breizerPath.addLine(to: self.mapView.projection.point(for: coordinate))
        }

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = breizerPath.reversing().cgPath
        shapeLayer.strokeColor = UIColor.green.cgColor
        shapeLayer.lineWidth = 4.0
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineJoin = kCALineJoinRound
        shapeLayer.lineCap = kCALineCapRound
        shapeLayer.cornerRadius = 5
        return shapeLayer
    }

    func animatePath(_ layer: CAShapeLayer) {
        let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
        pathAnimation.duration = 6
        //pathAnimation.delegate = self
        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        pathAnimation.fromValue = Int(0.0)
        pathAnimation.toValue = Int(1.0)
        pathAnimation.repeatCount = 100
        layer.add(pathAnimation, forKey: "strokeEnd")
    }

Добавлено в GoogleMapView пользователем

    let shapelayer: CAShapeLayer = self.layer(from: path!)
    self.animatePath(shapelayer)
    self.mapView.layer.addSublayer(shapelayer)

Я получаю следующий вывод


person Elangovan    schedule 06.03.2017    source источник
comment
попробуйте добавить эту строку как UIViewController's view subview, а не GMSMapView   -  person JuicyFruit    schedule 09.03.2017


Ответы (6)


Я создаю GMSPath анимацию с путем coordinate

введите описание изображения здесь

Цель C

интерфейс

@interface MapWithTracking () 

@property (weak, nonatomic) IBOutlet GMSMapView *mapView;

@property (nonatomic,strong) GMSMutablePath *path2;

@property (nonatomic,strong)NSMutableArray *arrayPolylineGreen;

@property (nonatomic,strong) GMSPolyline *polylineBlue;

@property (nonatomic,strong) GMSPolyline *polylineGreen;

@end

реализация

-(void)viewDidLoad {
    _arrayPolylineGreen = [[NSMutableArray alloc] init];
    _path2 = [[GMSMutablePath alloc]init];
}

Получите GMSPath и создайте синюю полилинию.

-(void)createBluePolyline(GMSPath *path) {

       // Here create a blue poly line
      _polylineBlue = [GMSPolyline polylineWithPath:path];
      _polylineBlue.strokeColor = [UIColor blueColor];
      _polylineBlue.strokeWidth = 3;
      _polylineBlue.map = _mapView;

       // animate green path with timer
       [NSTimer scheduledTimerWithTimeInterval:0.003 repeats:true block:^(NSTimer * _Nonnull timer) {
             [self animate:path];
       }];

}

Анимация зеленой полилинии

Добавление координат к пути 2 и назначение на карту

-(void)animate:(GMSPath *)path {

    dispatch_async(dispatch_get_main_queue(), ^{
        if (i < path.count) {
            [_path2 addCoordinate:[path coordinateAtIndex:i]];
            _polylineGreen = [GMSPolyline polylineWithPath:_path2];
            _polylineGreen.strokeColor = [UIColor greenColor];
            _polylineGreen.strokeWidth = 3;
            _polylineGreen.map = _mapView;
            [arrayPolylineGreen addObject:_polylineGreen];
            i++;
        }
        else {
            i = 0;
            _path2 = [[GMSMutablePath alloc] init];

            for (GMSPolyline *line in arrayPolylineGreen) {
                line.map = nil;
            }

        }
    });
}
person Harshal Valanda    schedule 16.03.2017
comment
Я проверил ваш код, он довольно легко читается. Но у меня есть один вопрос: всегда ли Polyline будет рисовать через 3 миллисекунды? Я имею в виду, что если широта и долгота являются сложной структурой, то для рисования пути потребуется время. (зависит от загрузки основного потока) - person Bhavin Chauhan; 16.10.2017
comment
Вы можете установить второй согласно вашему требованию. Если есть более сложная структура пути, то это работает как шарм, потому что в основном потоке мы не выполняем сложную операцию. вы также можете сделать это фоновым потоком, но анимация не работает как шарм. - person Harshal Valanda; 16.10.2017
comment
да, проблема в том, что если я использую эту анимацию с 3 миллисекундами, то это не всегда одна и та же анимация на всех устройствах. Я использовал DispatchQueue для этой операции. Это нормально или мне нужно использовать nsTimer? Не могли бы вы посоветовать мне? - person Bhavin Chauhan; 16.10.2017
comment
В приведенном выше случае я буду использовать таймер, потому что я добавлял новую координату пути каждые 0,003 секунды, поэтому он работает на всех устройствах. с моей точки зрения, время — это простой способ использовать такую ​​операцию. - person Harshal Valanda; 16.10.2017
comment
я просто добавлю новую точку и Polyline на карту и уберу после соревнований. вы можете найти способ использовать только один polyline, чтобы использовать мало памяти и повысить производительность. в приведенном выше случае я использую массив полилиний. - person Harshal Valanda; 16.10.2017
comment
Да, я использовал тот же код, и мне также потребовалась та же операция, что и вы. Но проблема в том, что когда есть закругленный путь, такой как круг, и путь по дороге не прост для вождения (например, прямая дорога), тогда это неправильная анимация, а также полилиния занимает больше времени, чем 3 миллисекунды, даже если я установил его на 3 миллисекунды. Не могли бы вы предложить мне? - person Bhavin Chauhan; 16.10.2017
comment
также я проверил, когда мы рисуем путь более 30 км и дорога не прямая, это занимает больше времени. - person Bhavin Chauhan; 16.10.2017
comment
Да, это займет больше времени, потому что, когда дорога круглая, у пути больше координат, чем у прямой дороги. вы можете попробовать пропустить одну координату, чтобы нарисовать путь, это только один из возможных способов. - person Harshal Valanda; 16.10.2017
comment
вы также можете видеть в приведенном выше примере, если путь прямой, то линия идет быстро, тогда круговой путь и расстояние также увеличиваются, а круговой путь - person Harshal Valanda; 16.10.2017
comment
как остановить таймер, сэр? - person kemdo; 26.03.2018
comment
@kemdo установить таймер как NSTimer * myTimer = [NSTimer scheduledTimerWithTimeInterval:110.0 target:self selector:@selector(targetMethod:) userInfo:nil repeats:YES]; Остановить таймер [myTimer invalidate]; - person Harshal Valanda; 26.03.2018

СВИФТ

Декларация

var polyline = GMSPolyline()
var animationPolyline = GMSPolyline()
var path = GMSPath()
var animationPath = GMSMutablePath()
var i: UInt = 0
var timer: Timer!

На Дарв Рут

func drawRoute(routeDict: Dictionary<String, Any>) {

        let routesArray = routeDict ["routes"] as! NSArray

        if (routesArray.count > 0)
        {
            let routeDict = routesArray[0] as! Dictionary<String, Any>
            let routeOverviewPolyline = routeDict["overview_polyline"] as! Dictionary<String, Any>
            let points = routeOverviewPolyline["points"]
            self.path = GMSPath.init(fromEncodedPath: points as! String)!

            self.polyline.path = path
            self.polyline.strokeColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
            self.polyline.strokeWidth = 3.0
            self.polyline.map = self.mapView

            self.timer = Timer.scheduledTimer(timeInterval: 0.003, target: self, selector: #selector(animatePolylinePath), userInfo: nil, repeats: true)
        }
    }

Анимировать путь

func animatePolylinePath() {
        if (self.i < self.path.count()) {
            self.animationPath.add(self.path.coordinate(at: self.i))
            self.animationPolyline.path = self.animationPath
            self.animationPolyline.strokeColor = UIColor.black
            self.animationPolyline.strokeWidth = 3
            self.animationPolyline.map = self.mapView
            self.i += 1
        }
        else {
            self.i = 0
            self.animationPath = GMSMutablePath()
            self.animationPolyline.map = nil
        }
    }

Не забудьте остановить таймер в представленииWillDisappear

self.timer.invalidate()

Выход

введите описание изображения здесь

person Elangovan    schedule 16.03.2017
comment
Tx для конвертации в Swift - person Harshal Valanda; 16.03.2017
comment
Большое спасибо за быстрое преобразование! - person Bishan; 28.05.2017
comment
HI в ОС версии 10.3.1 (iphone 5) работает не идеально. несколько раз становятся медленными и быстрыми в устройстве. В iphone 6 он стал медленным в OS 10.3.1, а в OS 10.0 он работает. - person Nisha; 10.10.2017
comment
Это вызывает проблемы с памятью, если приложение находится в фоновом режиме, таймер все еще работает, и это приводит к завершению работы приложения с потреблением памяти. - person JeeVan TiWari; 30.08.2018
comment
Вы пробовали с этим с маршрутом выстрела. Анимация не плавная для коротких маршрутов, таких как 2-3 км. - person Mahendra; 06.08.2019
comment
Я согласен с @Mahendra. На коротких маршрутах анимация очень быстрая. Можем ли мы его как-то замедлить? - person Sagun Raj Lage; 21.01.2020
comment
мы можем сделать это с транзакцией CA? - person jayant rawat; 14.02.2020

Перемещайте слой всякий раз, когда карта перемещается, реализуя делегат mapView:didChangeCameraPosition:.

person Jon Rose    schedule 14.03.2017
comment
Я создал путь breizerpath из координат GMSPath и создал CAShapeLayer из пути breizerpath. как изменить рамку слоя на cameraPosition? @Джон - person Elangovan; 15.03.2017
comment
Вы можете преобразовать координату в точку и точку в координату, используя свойство проекции в GMSMapView. см. developers.google.com/maps/documentation/ios-sdk/reference / - person Jon Rose; 15.03.2017
comment
@JonRose, можете ли вы предоставить код, который вы использовали для анимации или перемещения слоя? - person jayant rawat; 14.02.2020
comment
@Elangovan у тебя есть какое-нибудь решение? - person jayant rawat; 12.03.2020

Это адаптация кода от Elangovan

Изменения, которые я сделал, заключались в том, чтобы удалить переменную из класса, чтобы она находилась только в функции, а также удалить #selector, который больше не нужен в iOS >= 10.

var timerAnimation: Timer!
var mapView:GMSMapView?



    func drawRoute(encodedString: String, animated: Bool) {

        if let path = GMSMutablePath(fromEncodedPath: encodedString) {

            let polyline = GMSPolyline(path: path)
            polyline.strokeWidth = 3.0
            polyline.strokeColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
            polyline.map = Singleton.shared.getMapView()

            if(animated){
                self.animatePolylinePath(path: path)
            }
        }
    }

    func animatePolylinePath(path: GMSMutablePath) {

        var pos: UInt = 0
        var animationPath = GMSMutablePath()
        let animationPolyline = GMSPolyline()
        self.timerAnimation = Timer.scheduledTimer(withTimeInterval: 0.003, repeats: true) { timer in

            pos += 1
            if(pos >= path.count()){
                pos = 0
                animationPath = GMSMutablePath()
                animationPolyline.map = nil
            }
            animationPath.add(path.coordinate(at: pos))
            animationPolyline.path = animationPath
            animationPolyline.strokeColor = UIColor.yellow
            animationPolyline.strokeWidth = 3
            animationPolyline.map = self.mapView
        }
    }


    func stopAnimatePolylinePath() {

        self.timerAnimation.invalidate()
    }
person Tiago Mendes    schedule 06.05.2018

Для этого вам нужно использовать GMSPolyline. Создайте экземпляр GMSPolyline, установите экземпляр GMSMapView в качестве родительской карты.

GMSPolyline* routeOverlay = // config
routeOverlay.map = // my GMSMapView instance

Это все. Вам не нужно делать ничего дополнительно, чтобы заставить его двигаться вместе с камерой карты. Он делает это автоматически.

person Tarun Tyagi    schedule 15.03.2017
comment
Вопрос задает способ анимировать этот путь, который я не думаю, что вы можете сделать с помощью GMSPolyline. - person Jon Rose; 15.03.2017
comment
@JonRose Вы можете создать экземпляр GMSPolyline с экземпляром GMSPath. Вы можете создать GMSPath со строкой encodedPath, полученной от API Google Places, для поиска маршрута между двумя местоположениями. Я предполагаю, что Элангован уже использует эту установку. - person Tarun Tyagi; 15.03.2017
comment
Можете ли вы оживить этот путь? - person Jon Rose; 15.03.2017
comment
@JonRose Анимация GMSPolyline невозможна. Обходной путь — использовать CAShapeLayer для рисования анимации и, как только анимация завершится, использовать реальную GMSPolyline. Ссылка - stackoverflow.com/questions/38221641/ - person Tarun Tyagi; 15.03.2017
comment
отличная идея. Теперь предположим, что вы хотите, чтобы анимация постоянно повторялась, чтобы она всегда была на экране. Как заставить shapeLayer двигаться при движении карты? - person Jon Rose; 15.03.2017
comment
Я не сталкивался с таким вариантом использования. В вопросе об этом тоже не упоминалось. Однако в этом случае нам нужно сделать так, чтобы shapeLayer следовал за движением карты. - person Tarun Tyagi; 15.03.2017

Вы можете создать переменную для shapeLayer и использовать методы GMSMapViewDelegate mapView(_mapView: GMSMapView, жест willMove: Bool) и mapView(_mapView: GMSMapView, idleAt position: GMSCameraPosition) для добавления и удаления слоя с карты. У этого подхода есть два недостатка: во-первых, слой не анимируется при перетаскивании (перемещении) карты, а во-вторых, слой всегда находится поверх всех других элементов карты, таких как маркеры, названия дорог, POI и т. д. Я не смог найти способ добавить этот слой в качестве подслоя непосредственно к наложению земли. Вы можете найти полный код ниже:

var polyLineShapeLayer:CAShapeLayer?

 var layerAdded = false

 var path = GMSPath()

 var polyLine:GMSPolyline!



// Add regular polyline to the map 

func addPolyLineWithEncodedStringInMap(_ encodedString:String) {
        self.polyLine = GMSPolyline(path: self.path)
        polyLine.strokeWidth = 3.8
        self.polyLine.strokeColor = .black
        polyLine.map = googleMapView

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
            self.addPolyLineShapeLayerToMapView()
            self.layerAdded = true
        }

    }


// Add CAshapeLayer to map 

  func layer(from path: GMSPath) -> CAShapeLayer {
        let breizerPath = UIBezierPath()
        let firstCoordinate: CLLocationCoordinate2D = path.coordinate(at: 0)
        breizerPath.move(to: self.googleMapView.projection.point(for: firstCoordinate))
        for i in 1 ..< Int((path.count())){
            print(path.coordinate(at: UInt(i)))
            let coordinate: CLLocationCoordinate2D = path.coordinate(at: UInt(i))
            breizerPath.addLine(to: self.googleMapView.projection.point(for: coordinate))
        }

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = breizerPath.cgPath
        shapeLayer.strokeColor = UIColor.lightGray.withAlphaComponent(0.8).cgColor
        shapeLayer.lineWidth = 4.0
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineJoin = kCALineJoinRound
        shapeLayer.lineCap = kCALineCapRound
        shapeLayer.cornerRadius = 5
        return shapeLayer
    }

    func animatePath(_ layer: CAShapeLayer) {
        let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
        pathAnimation.duration = 2
        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        pathAnimation.fromValue = Int(0.0)
        pathAnimation.toValue = Int(1.0)
        pathAnimation.repeatCount = 200
        layer.add(pathAnimation, forKey: "strokeEnd")
    }

    func addPolyLineShapeLayerToMapView(){

        polyLineShapeLayer = self.layer(from: self.path)

        if let polyLineShapeLayer = polyLineShapeLayer{
            self.animatePath(polyLineShapeLayer)
            self.googleMapView.layer.addSublayer(polyLineShapeLayer)
            polyLineShapeLayer.zPosition = 0

        }

    }


// Delegate methods to control the polyline 


// whenever map is about to move, if layer is already added, remove the layer from superLayer 

    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
        if layerAdded{
            DispatchQueue.main.async {
                self.polyLineShapeLayer?.removeFromSuperlayer()
                self.polyLineShapeLayer = nil
            }
        }
    }

// when map is idle again(var layerAdded:bool ensures that additional layer is not added initially when the delegate method is fired) add new instance of polylineShapeLayer to the map with current projected coordinates. 

    func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
        if self.layerAdded{
            self.addPolyLineShapeLayerToMapView()
        }

    }
person Shrikant Chidgopkar    schedule 18.08.2018