SwiftUI集成Mapbox测距工具问题:折线与距离计算报错求助
SwiftUI + Mapbox 地图测距功能问题修复
问题分析与修复方案
1. PolylineAnnotation 无法正常工作的修复
- 问题原因:
lineArray是包含可选类型的数组[CLLocationCoordinate2D?],但PolylineAnnotation的lineCoordinates参数要求非可选的[CLLocationCoordinate2D]。- 代码中错误使用
lineArray?,且未判断两个点是否都存在就尝试绘制折线。
- 修复步骤:
- 过滤数组中的
nil值,转换为非可选数组。 - 仅当两个点都存在时,才渲染折线。
- 过滤数组中的
2. CoreLocation distance 函数编译失败的修复
- 问题原因:
distance(from:)是CLLocation类的方法,而非CLLocationCoordinate2D的方法,直接调用会报错。- 初始化
@State var distanceTwoPoints时,使用未初始化的可选值调用方法,语法错误。
- 修复步骤:
- 将
CLLocationCoordinate2D转换为CLLocation实例后再计算距离。 - 将距离计算改为计算属性,仅当两个点都存在时计算距离,否则返回提示文本。
- 将
完整修复代码
import SwiftUI @_spi(Experimental) import MapboxMaps import CoreLocation struct TestMap: View { // 测距状态控制 @State private var isMeasureOn = false // 存储两个触摸点 @State private var touchPoint1: CLLocationCoordinate2D? @State private var touchPoint2: CLLocationCoordinate2D? // 计算两点间距离(计算属性) private var distanceText: String { guard let point1 = touchPoint1, let point2 = touchPoint2 else { return "请点击地图选择两个点" } let location1 = CLLocation(latitude: point1.latitude, longitude: point1.longitude) let location2 = CLLocation(latitude: point2.latitude, longitude: point2.longitude) let distance = location1.distance(from: location2) return String(format: "%.2f 米", distance) } // 过滤后的非可选坐标数组 private var validCoordinates: [CLLocationCoordinate2D] { [touchPoint1, touchPoint2].compactMap { $0 } } var body: some View { ZStack { Map(initialViewport: .camera(center: CLLocationCoordinate2D(latitude: 55.681, longitude: 12.59), zoom: 12)) { if isMeasureOn { // 渲染第一个标记点 if let point1 = touchPoint1 { CircleAnnotation(centerCoordinate: point1) .circleRadius(5) .circleColor(StyleColor(.black)) .circleOpacity(1) } // 渲染第二个标记点 if let point2 = touchPoint2 { CircleAnnotation(centerCoordinate: point2) .circleRadius(5) .circleColor(StyleColor(.black)) .circleOpacity(1) } // 仅当有两个有效点时渲染折线 if validCoordinates.count == 2 { PolylineAnnotation(lineCoordinates: validCoordinates) .lineColor(StyleColor(.black)) .lineWidth(2) } } } .onMapTapGesture { context in guard isMeasureOn else { return } if touchPoint1 == nil { touchPoint1 = context.coordinate touchPoint2 = nil // 重置第二个点,支持重新选择第一个点后再选第二个 } else { touchPoint2 = context.coordinate } } // 控制按钮与距离显示 VStack { Button { isMeasureOn.toggle() if !isMeasureOn { // 关闭测距时重置所有状态 touchPoint1 = nil touchPoint2 = nil } } label: { VStack { Image("ICO-RULER") .font(.custom("Merriweather-Regular", size: 17)) .foregroundStyle(isMeasureOn ? .white : .black) .padding(10) .frame(maxWidth: 50, maxHeight: 50, alignment: .center) .background(isMeasureOn ? Color("CPHColor") : Color.white) .shadow(color: Color.black.opacity(0.15), radius: 0, x: 4.0, y: 4.0) // 仅在测距开启时显示距离 if isMeasureOn { Text(distanceText) .background(Color.white) .padding(10) .font(.caption) } } } .padding() .frame(maxWidth: .infinity, alignment: .topTrailing) } } } }
额外优化说明
- 关闭测距功能时自动重置标记点和距离状态,避免下次开启时残留旧数据。
- 支持重新选择第一个点(点击第一个点后再次点击地图会重置第二个点,重新选择第一个点)。
- 距离显示保留两位小数,提升可读性。
- 调整按钮布局,确保距离文本显示在按钮下方,布局更合理。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

