iOS MapKit缩放时出现Triangulator多边形三角剖分失败警告如何解决
问题修复方案
1. 三角化失败警告([VKDefault] Triangulator failed to fully triangulate polygon MapKit)修复
核心原因
你当前的代码没有正确解析GeoJSON的多边形结构:
- GeoJSON中
Polygon类型的坐标结构为[外环坐标数组, 内环1坐标数组, 内环2坐标数组...],你仅取了第一个外环数组,完全丢弃了内环(多边形内的洞)结构,不符合MKPolygon的创建规范 - 没有对坐标做基础合法性校验,连续重复坐标、首尾重复坐标、坐标绕向错误都会导致MapKit三角化失败
修复步骤
第一步:新增坐标预处理方法
// 预处理坐标:移除连续重复点、可选移除首尾重复点 func preprocessCoordinates(_ coords: [CLLocationCoordinate2D], removeTailDuplicate: Bool = true) -> [CLLocationCoordinate2D] { guard !coords.isEmpty else { return [] } var result = [CLLocationCoordinate2D]() var prev: CLLocationCoordinate2D? for coord in coords { if let prev = prev, prev.latitude == coord.latitude, prev.longitude == coord.longitude { continue } result.append(coord) prev = coord } // 移除首尾重复点 if removeTailDuplicate, let first = result.first, let last = result.last, first.latitude == last.latitude, first.longitude == last.longitude { result.removeLast() } return result }
第二步:修复多边形创建逻辑,正确处理内外环
// 从GeoJSON的Polygon结构生成MKPolygon func polygonFromGeoJSONPolygon(_ geoPolygon: [[[Double]]]) -> MKPolygon? { guard !geoPolygon.isEmpty else { return nil } // 处理外环 let outerCoord = geoPolygon[0].map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } let processedOuter = preprocessCoordinates(outerCoord) guard processedOuter.count >= 3 else { return nil } // 处理内环 var innerPolygons = [MKPolygon]() if geoPolygon.count > 1 { for i in 1..<geoPolygon.count { let innerCoord = geoPolygon[i].map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } let processedInner = preprocessCoordinates(innerCoord) guard processedInner.count >= 3 else { continue } innerPolygons.append(MKPolygon(coordinates: processedInner, count: processedInner.count)) } } return MKPolygon(coordinates: processedOuter, count: processedOuter.count, interiorPolygons: innerPolygons) }
第三步:替换原有addPolygonsToMap中的多边形创建逻辑
func addPolygonsToMap() { let fileURL = Bundle.main.url(forResource: "subregion", withExtension: "json")! let decoder = JSONDecoder() do { let data = try Data(contentsOf: fileURL) let subregions = try decoder.decode([SubRegion].self, from: data) for each in subregions { let geometry = each.geometry let geometryType = each.geometry.type if geometryType == PolygonType.polygon.rawValue { let coords = geometry.coordinates as! Polygon if let polygon = polygonFromGeoJSONPolygon(coords) { polygon.title = each.name // 顺便给polygon赋值name,你之前的annotation逻辑需要用title mapView.addOverlay(polygon) } } else if geometryType == PolygonType.multiPolygon.rawValue { let coords = geometry.coordinates as! MultiPolygon var polygons:[MKPolygon] = [] coords.forEach { geoPolygon in if let polygon = polygonFromGeoJSONPolygon(geoPolygon) { polygon.title = each.name polygons.append(polygon) } } guard !polygons.isEmpty else { continue } let multiPolygon = MKMultiPolygon(polygons) mapView.addOverlay(multiPolygon) } } } catch let error { print("Error", error) } }
2. 不可见区域样式请求警告([VKDefault] Style Z is requested for an invisible rect)修复
核心原因
该警告是你请求了当前地图不可见区域的元素渲染样式导致的,你的代码有两个触发点:
- 点击地图时遍历所有overlay的renderer修改颜色,不管该overlay是否在当前可见区域
- AnnotationView没有正确复用,你用
annotation.title!作为reuseIdentifier,相当于每个annotation都创建新的View,不可见区域的annotation也会触发样式请求
修复步骤
第一步:修改annotationView的复用逻辑
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard !(annotation is MKUserLocation) else { return nil } // 跳过系统定位蓝点 let reuseId = "LabelAnnotation" // 统一复用ID var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? LabelAnnotationView if annotationView == nil { annotationView = LabelAnnotationView(annotation: annotation, reuseIdentifier: reuseId) } annotationView?.annotation = annotation return annotationView }
第二步:操作renderer前先判断是否在可见区域
@objc func mapTapped(_ gesture: UITapGestureRecognizer) { let point = gesture.location(in: self.mapView) let coordinate = self.mapView.convert(point, toCoordinateFrom: nil) let mappoint = MKMapPoint(coordinate) let visibleRect = mapView.visibleMapRect for renderer in self.mapView.polygonRenderers { // 先判断overlay是否在可见区域,不在直接跳过 guard MKMapRectContainsRect(visibleRect, renderer.overlay.boundingMapRect) else { continue } let tapPoint = renderer.point(for: mappoint) if renderer.path.contains(tapPoint) && activeRendersContains(renderer) { renderer.strokeColor = UIColor.blue.withAlphaComponent(0.4) renderer.fillColor = UIColor.clear removeActive(renderer) } else if renderer.path.contains(tapPoint) { renderer.fillColor = UIColor.blue.withAlphaComponent(0.4) renderer.strokeColor = UIColor.blue appendActive(renderer) } } }
内容的提问来源于stack exchange,提问作者Waylan Sands
相关产品推荐
相关产品推荐

