Почему 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)
  }

person Paul Seagul    schedule 11.03.2019    source источник


Ответы (1)


Да, я заметил это после обновления 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)]
    }
person jeremy wilson    schedule 18.05.2021