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

SwiftUI中MKMapView的selectableMapFeatures选中功能失效问题

问题:MapKit兴趣点选中功能因SwiftUI状态更新失效

问题现象

设置mapView.selectableMapFeatures = [.pointsOfInterest]后,原本可正常点击选中地图上的兴趣点大头针,但在根视图用@State变量跟踪选中状态后,点击选中功能完全失效;移除对该状态变量的引用(比如删掉显示选中标题的Text),地图又能正常响应选中操作。尝试用StateObject结合EnvironmentObject传递状态,问题依然存在。

问题根源

  1. 错误的注解管理:updateUIView方法中每次调用view.removeAnnotations(view.annotations),会把系统自动生成的兴趣点注解(MKMapFeatureAnnotation)一并删除。当@State变量更新触发SwiftUI重绘时,updateUIView执行,此时用户刚选中的兴趣点注解已被移除,导致后续操作失效。
  2. 地图实例重复创建:MapView结构体把MKMapView作为存储属性,由于SwiftUI结构体是值类型,每次状态更新都会重新初始化MapView,导致新的MKMapView实例被创建,之前的选中状态和配置丢失。

修复方案

  • 仅管理自定义注解,不删除系统的兴趣点注解;
  • 在makeUIView中创建MKMapView实例,不将其作为MapView结构体的属性;
  • 优化updateUIView逻辑,仅在需要时更新自定义注解,而非每次删除重建。

修改后的代码

ContentView

import MapKit
import SwiftUI

struct ContentView: View {
    @State var selected: MKAnnotation?

    var body: some View {
        VStack {
            MapView(
                coordinate: .init(latitude: 37.3346, longitude: -122.0090),
                setSelected: { selected = $0 }
            )
            if let title = selected?.title {
                Text(title ?? "No Name")
            }
        }
    }
}

MapView

struct MapView: UIViewRepresentable {
    let coordinate: CLLocationCoordinate2D
    var title: String? = nil
    var setSelected: ((MKAnnotation?) -> Void)?

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView(frame: .zero)
        mapView.delegate = context.coordinator
        mapView.isUserInteractionEnabled = true
        mapView.selectableMapFeatures = [.pointsOfInterest]
        mapView.setRegion(.init(center: coordinate, span: .init(latitudeDelta: 0.02, longitudeDelta: 0.02)), animated: true)
        
        // 仅添加一次自定义注解
        if let title = title {
            let annotation = Annotation(title: title, coordinate: coordinate)
            mapView.addAnnotation(annotation)
        }
        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        // 只更新自定义注解的坐标和标题,不删除所有注解
        for annotation in view.annotations {
            if let customAnnotation = annotation as? Annotation {
                customAnnotation.coordinate = coordinate
                customAnnotation.title = title
                view.view(for: customAnnotation)?.setNeedsDisplay()
                return
            }
        }
        // 如果没有自定义注解,再添加
        if let title = title {
            let annotation = Annotation(title: title, coordinate: coordinate)
            view.addAnnotation(annotation)
        }
    }

    func makeCoordinator() -> MapViewCoordinator {
        MapViewCoordinator(self)
    }

    class MapViewCoordinator: NSObject, MKMapViewDelegate {
        var parent: MapView

        init(_ parent: MapView) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
            if let featureAnnotation = annotation as? MKMapFeatureAnnotation {
                parent.setSelected?(featureAnnotation)
            }
        }
    }

    class Annotation: NSObject, MKAnnotation {
        var title: String?
        var coordinate: CLLocationCoordinate2D

        init(title: String?, coordinate: CLLocationCoordinate2D) {
            self.title = title
            self.coordinate = coordinate
        }
    }
}

关键修改点

  1. 移除了MapView中作为存储属性的mapView,改为在makeUIView中创建实例,避免每次重绘都生成新地图;
  2. updateUIView不再删除所有注解,仅查找并更新自定义的Annotation,保留系统的兴趣点注解;
  3. 自定义Annotation的title和coordinate改为可变属性,支持在updateUIView中更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:33:10