首页 > 解决方案 > 为什么 MKPolyline 在用户绘图时滞后?

问题描述

当用户在屏幕上移动手指时,我想绘制一条折线。我根据一些文章编写代码,它可以工作到一定程度。在某些时候,一切都开始滞后,应用程序崩溃。

我试图清理叠加层并减少点,但这对我没有帮助。

  var rendersWorkaround: [MKOverlayRenderer] = []
  var currentRender: MKOverlayRenderer?

  var points: [CLLocationCoordinate2D] = []

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.rendersWorkaround.forEach { item in
      self.mapView.removeOverlay(item.overlay)
    }
    self.rendersWorkaround = []
    self.points = []
    if let touch = touches.first {
      let coordinate = mapView.convert(touch.location(in: mapView), toCoordinateFrom: mapView)
      points.append(coordinate)
    }
  }

  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

//    if self.lastTouchTime != nil && (Date().timeIntervalSinceReferenceDate - self.lastTouchTime!) < 0.025 { return }
    if self.rendersWorkaround.count > 10 {
      let countForDelete: Int = Int((Double(self.rendersWorkaround.count) / 2.0).rounded())
      for index in 0...countForDelete {
        self.mapView.removeOverlay(self.rendersWorkaround[index].overlay)
      }
      self.rendersWorkaround.removeSubrange(0...countForDelete)
    }
    if let touch = touches.first {
      let coordinate = mapView.convert(touch.location(in: mapView), toCoordinateFrom: mapView)
      points.append(coordinate)
      if points.count > 299 {
        points = points.indices.compactMap { return $0 % 2 != 0 ? points[$0] : nil }
      }
      let polyline = MKPolyline(coordinates: points, count: points.count)
      mapView.addOverlay(polyline)
    }
  }

  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    let polygon = MKPolygon(coordinates: &points, count: points.count)
    mapView.addOverlay(polygon)
    points = []
  }

  func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is MKPolyline {
      let polylineRenderer = MKPolylineRenderer(overlay: overlay)
      polylineRenderer.strokeColor = .orange
      polylineRenderer.lineWidth = 5
      self.rendersWorkaround.append(polylineRenderer)
      self.currentRender = polylineRenderer
      return polylineRenderer
    } else if overlay is MKPolygon {
      let polygonView = MKPolygonRenderer(overlay: overlay)
      polygonView.fillColor = .magenta
      self.rendersWorkaround.append(polygonView)
      return polygonView
    }

    return  MKTileOverlayRenderer(overlay: overlay)
  }

标签: swiftmapkitmkmapviewmkpolyline

解决方案


是的,我在 iOS 13 更新后注意到了这一点。Apple 做了很多地图更新,现在“rendererFor 覆盖”已经滞后。我不知道为什么。我最终移动到覆盖地图的 UIView。用户可以在该视图上绘制然后我将接触点转换为 mapView。所以最后会调用 rendererFor 。

此页面上有一个答案,他们正在做类似的事情。iOS + MKMapView 基于用户触摸的绘图

这是我的代码:

import Foundation

struct DrawViewViewModel {
    let lineColor: UIColor
    let lineWidth: CGFloat
}

protocol DrawViewDelegate: AnyObject {
    func touchesEnded(points: [CGPoint])
}

class DrawView: UIView {
    weak var delegate: DrawViewDelegate?
    private var lineArray: [CGPoint] = [CGPoint]()
    var firstPoint = CGPoint.zero
    
    var viewModel: DrawViewViewModel?
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        // 2
        guard let touch = touches.first else { return }
        firstPoint = touch.location(in: self)
        
        // 3
        lineArray.append(firstPoint)
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let currentPoint = touch.location(in: self)
        lineArray.append(currentPoint)
        setNeedsDisplay()
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let currentPoint = touch.location(in: self)
        lineArray.append(currentPoint)
        delegate?.touchesEnded(points: lineArray)
        
        //delete draw lines
        lineArray = []
        setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        draw(inContext: context)
    }
    
    func draw(inContext context: CGContext) {
        
        // 2
        if let VM = viewModel {
            context.setLineWidth(VM.lineWidth)
            context.setStrokeColor(VM.lineColor.cgColor)
        }
        context.setLineCap(.round)
        
        // 3
        for (index, point) in lineArray.enumerated() {
            
            // 4
            context.beginPath()
            context.move(to: point)
            if index+1 < lineArray.count {
                context.addLine(to: lineArray[index+1])
            }
            
            context.strokePath()
        }
    }
}


其次是我的 MKMapView 正在使用的代表:

//MARK - DrawViewDelegate
    func touchesEnded(points: [CGPoint]) {
        let mapPoints = convertPointsToCords(points: points)
        let polygon = MKPolygon(coordinates: mapPoints, count: mapPoints.count)
        
        let worldPoly = getWorldPoly()
        let polygonInverted = MKPolygon(coordinates: worldPoly, count: worldPoly.count, interiorPolygons: [polygon]) //create an inverted polygon
        
        coreMapView.addOverlay(polygonInverted) //Add polygon areas
        
        //Then run the search
        // update search criteria
        updateSearchCriteria(polygonBoundPoints: mapPoints)
        
        // move map
        coreMapView.zoomToPolygon(polygon: polygon)// make sure to call this after we hang the DnDSign
        
        // allow map movement and disable drawing
        coreMapView.isScrollEnabled = true
        coreMapView.isZoomEnabled = true
        coreMapView.isUserInteractionEnabled = true
    }
func convertPointsToCords(points:[CGPoint]) -> [CLLocationCoordinate2D] {
        var mapPoints = [CLLocationCoordinate2D]()
        for point in points {
            let convertedPoint = mapDrawView.convert(point, to: coreMapView)
            let coordinate = coreMapView.convert(convertedPoint, toCoordinateFrom: coreMapView)
            mapPoints.append(coordinate)

        }
        return mapPoints
    }

如果你想要像我这里的倒置多边形是世界多边形,所以你可以把你的放在里面。

func getWorldPoly() -> [CLLocationCoordinate2D] {
        return [CLLocationCoordinate2D(latitude: 90, longitude: 0),
                CLLocationCoordinate2D(latitude: 90, longitude: 180),
                CLLocationCoordinate2D(latitude: -90, longitude: 180),
                CLLocationCoordinate2D(latitude: -90, longitude: 0),
                CLLocationCoordinate2D(latitude: -90, longitude: -180),
                CLLocationCoordinate2D(latitude: 90, longitude: 180)]
    }

推荐阅读