Meet MapKit for SwiftUI
Swift & UI 进阶 20m

认识 SwiftUI 版 MapKit

Meet MapKit for SwiftUI

2023年6月5日

在 Apple 官方观看视频

一句话判断

一行代码就能在 SwiftUI 中嵌入交互式地图——然后通过 Marker、Annotation、Look Around、路线叠加、真实地形等功能,构建出完整的地图体验。这场 Session 用一个波士顿旅行规划器从零演示了全部能力。

这场 Session 讲了什么

MapKit 团队的 Jeff 通过构建一个完整的波士顿旅行规划应用,展示了 SwiftUI 版 MapKit 的全套能力。

核心是 MapKit for SwiftUI 的大幅扩展。过去在 SwiftUI 中使用地图功能受限,现在通过 MapContentBuilder 闭包,可以像在 List 中添加 View 一样在地图上添加内容。

Session 覆盖了以下功能:Marker(标记点,标准气球样式)、Annotation(自定义 SwiftUI View 标记)、地图样式切换(标准/卫星/混合)、真实地形高程、Look Around 街景集成、路线叠加(driving route overlay)、搜索结果展示、标记选择交互、用户位置按钮等地图控件。

地图会自动调整视野来展示所有添加的内容。搜索结果使用 MKMapItem 类型的 Marker,自动显示对应的图标和颜色(如海滩显示伞形图标)。

值得深挖的点

MapContentBuilder 的设计哲学:和 SwiftUI 的 ViewBuilder 一样,MapContentBuilder 使用结果构建器模式。你可以在闭包中用 ForEach 遍历数据添加多个标记或叠加层。所有地图内容类型(Marker、Annotation、Overlay)都用同一个 builder 添加。

Marker vs Annotation 的选择:Marker 使用系统标准气球样式,支持自动图标和着色——配合 MKMapItem 使用时尤其方便,系统会自动选择合适的图标。Annotation 则完全自定义——你提供一个 SwiftUI View,它被渲染在地图的指定坐标上。

safeAreaInset 的正确使用:在地图上方叠加自定义 UI 时,用 safeAreaInset 确保不会遮挡地图内容、Apple Maps logo 和法律链接。这是 MapKit SwiftUI 集成中的一个重要细节。

Look Around 集成:可以在地图中直接嵌入 Look Around 街景视图,让用户在规划行程时预览目的地。这是 MapKit 中之前需要大量代码才能实现的功能。

代码片段

// 一行代码嵌入交互式地图
Map { }
// 使用 MapContentBuilder 添加标记
Map {
    Marker("Parking", coordinate: parkingCoordinate)
        .tint(.blue)

    // 自定义 SwiftUI View 作为标记
    Annotation("Start", coordinate: startCoordinate, anchor: .bottom) {
        ZStack {
            Circle().fill(.blue)
            Image(systemName: "figure.walk")
        }
    }
}
.mapStyle(.standard(elevation: .realistic)) // 启用真实地形
// 搜索并展示结果
@State private var searchResults: [MKMapItem] = []

Map {
    ForEach(searchResults, id: \.self) { item in
        Marker(item: item)  // 自动使用 map item 的图标和颜色
    }
}
.safeAreaInset(edge: .bottom) {
    // 搜索按钮,不遮挡地图内容
    HStack {
        Button("Playgrounds") { search("playground") }
        Button("Beaches") { search("beach") }
    }
    .padding()
}

func search(_ query: String) {
    let request = MKLocalSearch.Request()
    request.naturalLanguageQuery = query
    request.region = MKCoordinateRegion(
        center: parkingCoordinate,
        span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    )
    MKLocalSearch(request: request).start { response, _ in
        searchResults = response?.mapItems ?? []
    }
}
// 路线叠加和地图样式
Map {
    Marker("Beach", coordinate: beachCoordinate)
    MapPolyline(route.polylines.first!)
        .stroke(.blue, lineWidth: 5)
}
.mapStyle(.imagery) // 卫星图
.mapControls {
    MapUserLocationButton()  // 用户位置按钮
    MapCompass()             // 指南针
}

最佳实践

  • 配合 MKMapItem 使用 Marker:搜索结果直接用 Marker(item: mapItem) 创建,系统自动提供合适的图标、颜色和标题。
  • 用 safeAreaInset 叠加 UI:不要用 ZStack 叠加,用 safeAreaInset 确保地图内容和系统控件不被遮挡。
  • 根据场景选择地图样式:标准地图适合导航和搜索,卫星图适合展示地点环境,混合模式兼顾两者。
  • 开启真实地形增强沉浸感elevation: .realistic 让地图从平面变成有立体感的体验,特别适合旅行和户外应用。
  • 利用自动视野调整:地图会自动缩放到合适的级别展示所有内容。如果需要手动控制,可以通过 camera position 相关 API 设置。

还有什么值得关注

  • MapKit for SwiftUI 现在支持所有 Apple 平台,包括 watchOS 和 tvOS。
  • Look Around 街景可以在地图视图内嵌入,也可以作为独立的全屏体验展示。
  • MapPolyline 支持自定义样式(颜色、线宽),可以展示驾车、步行等不同类型的路线。
  • Session 中的旅行规划器是一个很好的参考项目,展示了如何将搜索、标记、路线、街景等功能组合在一起。
  • 新的 Map Controls API(MapUserLocationButton、MapCompass 等)让地图控件的添加变得和普通 SwiftUI 按钮一样简单。
WWDC 2023