如何在 GMSMapView 中获取动画折线路线,以便在移动地图时随地图一起移动?

Posted

技术标签:

【中文标题】如何在 GMSMapView 中获取动画折线路线,以便在移动地图时随地图一起移动?【英文标题】:How to get animated polyline route in GMSMapView, so that it move along with map when map is moved? 【发布时间】:2017-07-26 00:30:09 【问题描述】:

我通过以下代码创建了像 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)

【问题讨论】:

尝试将此行添加为UIViewController's 查看子视图,而不是GMSMapView 【参考方案1】:

SWIFT

声明

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
        
    

不要忘记在 viewWillDisappear 中停止计时器

self.timer.invalidate()

输出

【讨论】:

Tx 快速转换 非常感谢您的快速转换! 操作系统版本 10.3.1(iphone 5) 中的 HI 不能完美运行。有时在设备中运行变得缓慢而快速。在 iphone 6 中,它在 os 10.3.1 中变得很慢,而在 os 10.0 中它的工作。 如果应用程序处于后台计时器仍在运行并导致应用程序因内存消耗而终止,则会导致内存问题 您是否尝试过使用此拍摄路线。对于 2-3 公里这样的短途路线,动画不流畅。【参考方案2】:

我正在使用路径 coordinate 创建 GMSPath 动画

目标 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;
            

        
    );

【讨论】:

我检查了你的代码,很容易阅读。但我有一个问题,折线是否总是在 3 毫秒后绘制?我的意思是,如果纬度和经度是复杂的结构,那么绘制路径需要时间。 (取决于主线程负载) 您可以根据自己的需要设置秒。如果路径结构更复杂,那么它就像魅力一样工作,因为在主线程中我们没有进行复杂的操作。您也可以在后台线程中执行此操作,但动画效果并不像魅力。 是的,问题是如果我使用 3 毫秒的动画,那么在所有设备中它并不总是相同的动画。我已经使用 DispatchQueue 进行此操作。没问题还是我需要使用nsTimer?你能给我建议吗? 在上述情况下,我将使用计时器,因为我每 0.003 秒添加一个新的路径坐标,因此它适用于所有设备。从我的角度来看,时间是使用这种操作的简单方法。 @kemdo 设置定时器为NSTimer * myTimer = [NSTimer scheduledTimerWithTimeInterval:110.0 target:self selector:@selector(targetMethod:) userInfo:nil repeats:YES]; 停止定时器[myTimer invalidate];【参考方案3】:

这是对 Elangovan 代码的改编

我所做的更改是从类中删除 var 使其仅在函数中,并且还删除了 iOS >= 10 中不再需要的 #selector。

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()
    

【讨论】:

【参考方案4】:

通过实现委托mapView:didChangeCameraPosition:,在地图移动时移动图层。

【讨论】:

我已经从 GMSPath 坐标创建了 breizerpath 路径,并从 breizerpath 创建了 CAShapeLayer。如何将图层框架更改为cameraPosition? @乔恩 您可以使用 GMSMapView 上的投影属性将坐标转换为点和点到坐标。见developers.google.com/maps/documentation/ios-sdk/reference/… @JonRose 你能提供你用来动画或移动图层的代码吗? @Elangovan 你有什么解决办法吗?【参考方案5】:

您需要为此使用GMSPolyline。创建一个GMSPolyline 实例,将您的GMSMapView 实例设置为它的父映射。

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

就是这样。你不需要做任何额外的事情来让它随着地图相机的移动而移动。它会自动执行此操作。

【讨论】:

该问题要求一种方法来为该路径设置动画,我认为您无法使用 GMSPolyline 进行动画处理 @JonRose 您可以使用 GMSPath 实例创建 GMSPolyline 实例。您可以使用从 Google Places API 接收到的 encodedPath 字符串创建 GMSPath,以查找两个位置之间的路线。我假设 Elangovan 已经在使用这个设置。 你能为那条路径设置动画吗? @JonRose 动画 GMSPolyline 是不可能的。一种解决方法是使用 CAShapeLayer 绘制动画,并在动画完成后立即使用实际的 GMSPolyline。参考 - ***.com/questions/38221641/… 好主意。现在假设您希望动画不断重复,因此它始终在屏幕上。地图移动时如何让 shapeLayer 移动?【参考方案6】:

您可以为 shapeLayer 创建一个变量并使用 GMSMapViewDelegate 方法 mapView(_ mapView: GMSMapView, willMove 手势: Bool) 和 mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) 从地图中添加和删除图层。这种方法有两个缺点,首先是当地图被拖动(移动)时图层没有动画,第二个缺点是图层总是位于所有其他地图元素(如标记、道路名称、兴趣点等)的顶部.我找不到将这一层作为子层直接添加到地面覆盖层的方法。您可以在下面找到完整的代码:

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()
        

    

【讨论】:

以上是关于如何在 GMSMapView 中获取动画折线路线,以便在移动地图时随地图一起移动?的主要内容,如果未能解决你的问题,请参考以下文章

在iOS中的GMSMapView上绘制两地之间的路线

如何在iphone中获取gmsmapview的坐标中心

如何在传单中的两个标记之间添加路线(折线)

如何在路线谷歌地图v2 android上绘制交互式折线

如何在流线型页面中为折线图制作动画

如何在 JavaScript 中围绕折线绘制多边形?