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

MapKit问题:SwiftUI(Xcode15)无法通过变量获取路线信息

解决SwiftUI地图应用路线信息存储与Sheet展示问题

核心问题分析

你当前的代码存在两个关键问题:

  1. 在Map视图闭包内定义的var myRoute是局部临时变量,仅在该闭包的单次构建周期内有效,无法被外部(比如Sheet)访问。
  2. 点击"Show Sheet"按钮时使用强制解包!获取路线信息,当route为nil时会直接崩溃,存在安全隐患。

解决方案

我们需要在获取路线数据的时机直接更新全局状态变量,同时避免强制解包:

  1. 移除无效局部变量:删除Map闭包内的var myRoute = route.steps.map...代码,SwiftUI的body是视图构建区域,不适合在此修改状态变量。
  2. 路线计算后同步更新状态:在getDirections函数中,获取到route后直接将步骤指令转换为字符串数组存入directions变量。
  3. 优化Sheet触发逻辑:点击按钮时只需切换showDirections状态,无需重复处理路线数据。

修改后的完整代码

import SwiftUI
import MapKit

extension CLLocationCoordinate2D {
    static let parking = CLLocationManager().location?.coordinate // CLLocationCoordinate2D(latitude: 42.354528, longitude: -71.068369)
}

struct ContentView: View {
        
    @State private var position: MapCameraPosition = .automatic
    @State private var visibleRegion: MKCoordinateRegion?
    @State private var searchResults: [MKMapItem] = []
    @State private var selectedResult: MKMapItem?
    @State private var route: MKRoute?
    @State private var showDirections = false
    @State private var directions: [String] = []
    @State private var myLocation = CLLocationManager().location?.coordinate
    
    @StateObject private var viewModel = ContentViewModel()
    
    var body: some View {
        
        Map(position: $position, selection: $selectedResult)
        {
            ForEach(searchResults, id: \.self) { result in
                Marker(item: result)
            }
            .annotationTitles(.hidden)
            
            UserAnnotation()
            
            if let route {
                MapPolyline(route)
                    .stroke(.blue, lineWidth: 5)
                var _ = print("ROUTE = \(route.steps.map { $0.instructions }.filter { !$0.isEmpty })")
            }
        }
        .onAppear()
        {
            viewModel.checkIfLocationServicesIsEnabled()
        }
        .mapStyle(.standard(elevation: .realistic))
        .safeAreaInset(edge: .bottom) {
            HStack {
                Spacer()
                VStack(spacing: 0) {
                    if let selectedResult {
                        ItemInfoView(selectedResult: selectedResult, route: route)
                            .frame(height: 128)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                            .padding([.top, .horizontal])
                    }
                    
                    Button("Show Sheet") {
                        showDirections.toggle()
                    }
                    .sheet(isPresented: $showDirections) {
                        VStack(spacing: 0) {
                          Text("Directions")
                            .font(.largeTitle)
                            .bold()
                            .padding()

                          Divider().background(Color(UIColor.systemBlue))
                            
                          List(directions, id: \.self) { instruction in
                              Text(instruction).padding()
                          }
                            
                          Button("Dismiss") {
                              showDirections.toggle()
                          }
                          .padding()
                          .background(Color.blue)
                          .foregroundColor(.white)
                          .cornerRadius(10)
                        }
                        .padding()
                    }
                    .padding()
                    
                    POI(position: $position, searchResults: $searchResults, visibleRegion: visibleRegion)
                        .padding(.top)
                        .frame(width: 350, height: 100)
                }
                Spacer()
            }
            .background(.thinMaterial)
            
        }
        .onChange(of: searchResults) {
            position = .automatic
        }
        .onChange(of: selectedResult) {
            getDirections()
        }
        .onMapCameraChange { context in
            visibleRegion = context.region
        }
        .mapControls {
            MapUserLocationButton()
            MapCompass()
            MapScaleView()
        }
    }
    
    func getDirections() {
        route = nil
        directions.removeAll()
        guard let selectedResult, let parkingCoordinate = CLLocationCoordinate2D.parking else { return }
        
        let request = MKDirections.Request()
        request.source = MKMapItem(placemark: MKPlacemark(coordinate: parkingCoordinate))
        request.destination = selectedResult
        
        Task {
            let directions = MKDirections(request: request)
            guard let response = try? await directions.calculate(), let route = response.routes.first else {
                print("Failed to calculate route")
                return
            }
            self.route = route
            self.directions = route.steps.map { $0.instructions }.filter { !$0.isEmpty }
            print("COUNT = \(self.directions.count)")
        }
    }
}

// 补充缺失的视图定义以保证编译通过
struct ContentViewModel: ObservableObject {
    func checkIfLocationServicesIsEnabled() {}
}

struct ItemInfoView: View {
    let selectedResult: MKMapItem
    let route: MKRoute?
    
    var body: some View {
        Text(selectedResult.name ?? "Unknown Location")
    }
}

struct POI: View {
    @Binding var position: MapCameraPosition
    @Binding var searchResults: [MKMapItem]
    let visibleRegion: MKCoordinateRegion?
    
    var body: some View {
        TextField("Search", text: .constant(""))
    }
}

#Preview {
    ContentView()
}

关键修改点说明

  • 在getDirections中增加parking坐标的可选绑定,避免强制解包崩溃。
  • 路线计算成功后直接将处理好的指令数组赋值给@State var directions,确保数据及时同步。
  • Sheet中的List改为直接遍历directions数组,代码更简洁安全。
  • 移除所有不必要的局部变量和强制解包操作,提升代码稳定性。

内容的提问来源于stack exchange,提问作者user427196

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:44:56