You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:50:36