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

如何在SwiftUI的MapKit Map View中添加MKOverlay覆盖层

解决方案

SwiftUI 内置的 Map 视图在 iOS 17 之前未提供原生的覆盖层配置参数,要实现 MKOverlay 相关能力,可根据项目最低支持版本选择以下两种方案:

方案1:兼容 iOS 14 及以上版本(UIKit 封装实现)

通过 UIViewRepresentable 封装 UIKit 原生的 MKMapView,保留原有交互逻辑的同时支持添加任意类型的覆盖层:

首先是封装的自定义视图代码:

import SwiftUI
import MapKit

struct CustomMapView: UIViewRepresentable {
    @Binding var region: MKCoordinateRegion
    let annotationItems: [City]
    let overlays: [MKOverlay]
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        mapView.setRegion(region, animated: false)
        // 添加覆盖层
        mapView.addOverlays(overlays)
        return mapView
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        // 同步区域更新
        if uiView.region.center.latitude != region.center.latitude ||
            uiView.region.center.longitude != region.center.longitude ||
            uiView.region.span.latitudeDelta != region.span.latitudeDelta ||
            uiView.region.span.longitudeDelta != region.span.longitudeDelta {
            uiView.setRegion(region, animated: true)
        }
        // 同步标注更新
        uiView.removeAnnotations(uiView.annotations)
        let annotations = annotationItems.map { city -> MKPointAnnotation in
            let anno = MKPointAnnotation()
            anno.coordinate = city.coordinate
            return anno
        }
        uiView.addAnnotations(annotations)
        // 同步覆盖层更新
        uiView.removeOverlays(uiView.overlays)
        uiView.addOverlays(overlays)
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    class Coordinator: NSObject, MKMapViewDelegate {
        // 覆盖层渲染配置
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            // 瓦片覆盖层渲染
            if let tileOverlay = overlay as? MKTileOverlay {
                return MKTileOverlayRenderer(overlay: tileOverlay)
            }
            // 多边形覆盖层渲染
            if let polygon = overlay as? MKPolygon {
                let renderer = MKPolygonRenderer(overlay: polygon)
                renderer.fillColor = .blue.withAlphaComponent(0.3)
                renderer.strokeColor = .blue
                renderer.lineWidth = 2
                return renderer
            }
            // 折线覆盖层渲染
            if let polyline = overlay as? MKPolyline {
                let renderer = MKPolylineRenderer(overlay: polyline)
                renderer.strokeColor = .red
                renderer.lineWidth = 3
                return renderer
            }
            return MKOverlayRenderer(overlay: overlay)
        }
        
        // 标注渲染配置
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            let pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "cityPin")
            pinView.tintColor = .red
            return pinView
        }
        
        // 同步区域拖拽后的绑定值
        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            DispatchQueue.main.async {
                if let binding = mapView.value(forKey: "regionBinding") as? Binding<MKCoordinateRegion> {
                    binding.wrappedValue = mapView.region
                }
            }
        }
    }
}

然后替换原有 ContentView 中的系统 Map 即可:

struct ContentView: View {
  @State private var cities: [City] = [
    City(coordinate: .init(latitude: 54.8985, longitude: 23.9036)),
    City(coordinate: .init(latitude: 54.6872, longitude: 25.2797)),
    City(coordinate: .init(latitude: 55.7033, longitude: 21.1443))
  ]
  @State private var region = MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 54.8, longitude: 23.8),
    span: MKCoordinateSpan(
      latitudeDelta: 5,
      longitudeDelta: 5
    )
  )
  // 配置需要添加的覆盖层,可同时添加多个不同类型的覆盖层
  private var overlays: [MKOverlay] {
      let tileOverlay = MKTileOverlay(urlTemplate: "https://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer/tile/{z}/{y}/{x}.jpg")
      // 示例多边形,可根据需求替换
      let polygon = MKPolygon(coordinates: [
        CLLocationCoordinate2D(latitude: 54.8985, longitude: 23.9036),
        CLLocationCoordinate2D(latitude: 54.6872, longitude: 25.2797),
        CLLocationCoordinate2D(latitude: 55.7033, longitude: 21.1443)
      ], count: 3)
      return [tileOverlay, polygon]
  }
  
  var body: some View {
    ZStack {
      // 替换为自定义的CustomMapView
      CustomMapView(region: $region, annotationItems: cities, overlays: overlays)
        .ignoresSafeArea()
      Circle()
        .fill(Color.blue)
        .opacity(0.3)
        .frame(width: 32, height: 32)
      VStack {
        Spacer()
        HStack {
          Spacer()
          Button {
            self.cities.append(City(coordinate: self.region.center))
          } label: {
            Image(systemName: "plus")
          }
          .padding()
          .background(.black.opacity(0.75))
          .foregroundColor(.white)
          .font(.title)
          .clipShape(Circle())
          .padding(.trailing)
        }
      }
    }
  }
}

方案2:最低支持 iOS 17 及以上版本(原生API实现)

iOS 17 之后 SwiftUI 新增了覆盖层相关的原生支持,无需封装UIKit组件,直接使用内置API即可:

struct ContentView: View {
  @State private var cities: [City] = [
    City(coordinate: .init(latitude: 54.8985, longitude: 23.9036)),
    City(coordinate: .init(latitude: 54.6872, longitude: 25.2797)),
    City(coordinate: .init(latitude: 55.7033, longitude: 21.1443))
  ]
  @State private var position: MapCameraPosition = .region(MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 54.8, longitude: 23.8),
    span: MKCoordinateSpan(
      latitudeDelta: 5,
      longitudeDelta: 5
    )
  ))
  // 瓦片覆盖层
  private let tileOverlay = MKTileOverlay(urlTemplate: "https://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer/tile/{z}/{y}/{x}.jpg")
  // 示例多边形
  private let polygon = MKPolygon(coordinates: [
    CLLocationCoordinate2D(latitude: 54.8985, longitude: 23.9036),
    CLLocationCoordinate2D(latitude: 54.6872, longitude: 25.2797),
    CLLocationCoordinate2D(latitude: 55.7033, longitude: 21.1443)
  ], count: 3)
  
  var body: some View {
    ZStack {
      Map(position: $position) {
        // 标注
        ForEach(cities) { city in
          MapPin(coordinate: city.coordinate, tint: .red)
        }
        // 瓦片覆盖层
        MapOverlay(tileOverlay)
        // 多边形覆盖层
        MapPolygon(polygon)
          .foregroundStyle(.blue.opacity(0.3))
          .stroke(.blue, lineWidth: 2)
      }
      .ignoresSafeArea()
      Circle()
        .fill(Color.blue)
        .opacity(0.3)
        .frame(width: 32, height: 32)
      VStack {
        Spacer()
        HStack {
          Spacer()
          Button {
            if let region = position.region {
              self.cities.append(City(coordinate: region.center))
            }
          } label: {
            Image(systemName: "plus")
          }
          .padding()
          .background(.black.opacity(0.75))
          .foregroundColor(.white)
          .font(.title)
          .clipShape(Circle())
          .padding(.trailing)
        }
      }
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 00:45:03