WWDC Quick Look 💓 By SwiftGGTeam
Meet MapKit for SwiftUI

Meet MapKit for SwiftUI

观看原视频

Highlight

MapKit 工程师 Jeff 用不到 30 分钟从零构建了一个完整的波士顿旅行规划应用,展示了 SwiftUI 版 MapKit 如何通过 MapContentBuilder 闭包和声明式 API 大幅降低地图集成门槛。

核心内容

在 SwiftUI 中使用地图曾经是个麻烦事。UIKit 时代的 MKMapView 需要桥接,自定义标注和覆盖物要写大量委托代码,相机控制更是需要手动计算区域和缩放级别。

01:42)Jeff 的演示从一个空 SwiftUI 项目开始。导入 MapKit,加一行 Map(),一个可交互的地图就出现了。没有委托,没有配置,没有桥接。

02:41)标注内容通过 MapContentBuilder 闭包添加,语法和往 List 里加 View 几乎一样。Marker 显示标准气球图标,Annotation 则可以放任意 SwiftUI View。地图会自动调整视野框住所有内容——这是默认行为,不需要手动计算。

04:36)地图样式用 mapStyle 修饰符切换。标准地图默认是平面展示,加上 .standard(elevation: .realistic) 后地形有了真实高程,桥梁和湖泊立刻有了立体感。还可以切到卫星图或混合模式。

09:10)相机控制通过 MapCameraPosition 状态实现。automatic 让地图自动框住内容,region 显示指定区域,item 聚焦某个地点,userLocation 跟随用户位置。当用户手动拖拽地图后,状态变为 positionedByUser,应用可以据此判断是否需要重新定位。

17:33)选择交互只需给 Map 加一个 selection 绑定。Marker 会自动显示选中动画。配合 onChange 可以在选中时获取路线、显示 Look Around 街景预览。

21:54)覆盖物同样通过内容构建器添加。MapPolyline 显示路线,MapPolygonMapCircle 标记区域。每个覆盖物可以指定 aboveRoadsaboveLabels 层级,控制与地图标签的叠放关系。

23:47)地图控件包括 MapUserLocationButtonMapCompassMapScaleView,通过 mapControls 修饰符自动布局。也可以当作普通 View 手动放置,此时需要用 mapScope 关联到特定地图实例。

详细内容

最小可用地图

import MapKit
import SwiftUI

struct ContentView: View {
    var body: some View {
        Map()
    }
}

关键点:

  • 一行代码获得可交互地图,支持拖拽、缩放、旋转
  • 自动处理用户定位授权和指南针方向

添加标注和自定义视图

struct ContentView: View {
    var body: some View {
        Map {
            // 标准 Marker,气球图标
            Marker("Parking", coordinate: .parkingGarage)
            
            // 自定义 Annotation,放任意 SwiftUI View
            Annotation("Custom Spot", coordinate: .customSpot) {
                ZStack {
                    Circle().fill(.blue)
                    Image(systemName: "car.fill")
                }
            }
        }
    }
}

关键点:

  • Marker 适合简单地点标记,系统自动处理图标和颜色
  • Annotation 的闭包里可以写完整的 SwiftUI 视图层级
  • anchor: .bottom 让视图底部对准坐标点,而非中心对准

基于搜索结果的动态标注

struct ContentView: View {
    @State private var searchResults: [MKMapItem] = []
    @State private var position: MapCameraPosition = .automatic
    
    var body: some View {
        Map(position: $position) {
            ForEach(searchResults, id: \.self) { item in
                Marker(item: item)
            }
        }
        .onChange(of: searchResults) {
            position = .automatic
        }
    }
    
    func search(for query: String) {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = query
        request.region = visibleRegion
        
        MKLocalSearch(request: request).start { response, _ in
            searchResults = response?.mapItems ?? []
        }
    }
}

关键点:

  • Marker(item:) 自动使用 MKMapItem 的名称、图标和色调
  • 搜索海滩会显示伞形图标,搜索游乐场显示滑梯图标
  • position = .automatic 让地图自动缩放到展示所有结果
  • 用户拖拽后搜索,需要手动重置 position 才能重新框选

相机位置状态

@State private var position: MapCameraPosition = .region(.boston)

// 显示指定区域
position = .region(MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 42.36, longitude: -71.06),
    span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
))

// 跟随用户位置,未授权时回退到指定区域
position = .userLocation(fallback: .region(.boston))

// 显示某个地点,自动计算合适的缩放级别
position = .item(MKMapItem(placemark: MKPlacemark(coordinate: .beach)))

关键点:

  • MapCameraPosition 是值类型,通过绑定与 Map 双向同步
  • 用户交互后状态变为 positionedByUser,应用可以检测并响应
  • onMapCameraChange 获取当前可见区域,支持连续更新或仅在交互结束时触发

路线覆盖物

Map {
    if let route {
        MapPolyline(route.polyline)
            .stroke(.blue, lineWidth: 5)
    }
}

// 获取路线
func getRoute(from source: MKMapItem, to destination: MKMapItem) {
    let request = MKDirections.Request()
    request.source = source
    request.destination = destination
    
    MKDirections(request: request).calculate { response, _ in
        route = response?.routes.first
    }
}

关键点:

  • MapPolyline 直接接受 MKPolyline,不需要坐标转换
  • StrokeStyle 支持虚线、渐变等效果
  • MapPolygonMapCircle 用于标记区域,可通过 aboveRoads / aboveLabels 控制覆盖物与道路、标签的叠放关系;aboveRoads 会让地图标签压在覆盖物之上,aboveLabels 会让覆盖物压在标签之上

地图控件

Map {
    // 内容
}
.mapControls {
    MapUserLocationButton()
    MapCompass()
    MapScaleView()
}

关键点:

  • mapControls 自动在合适位置布局控件
  • 手动放置时用 mapScope 关联:.mapScope(mapScope)
  • macOS 额外提供 MapZoomStepperMapPitchSlider

核心启发

  • 给本地生活类 app 加一张可交互地图:餐厅、酒店、活动类应用可以用 MapKit for SwiftUI 快速实现”附近推荐”功能。Marker 自动显示类别图标,Look Around 提供街景预览,用户不需要离开应用就能判断地点是否合适。

  • 用地图做数据可视化:如果应用有地理位置相关的数据(如配送轨迹、运动路线、旅行足迹),用 MapPolyline 和自定义 Annotation 直接展示在地图上,比列表更直观。配合 onMapCameraChange 可以只加载当前视野内的数据点。

  • 做一款旅行规划工具:参考 session 中的波士顿行程规划思路——用搜索找景点、用 Marker 标记、用路线连接、用 Look Around 预览。SwiftUI 的声明式语法让这种多步骤交互的实现成本大幅降低。

关联 Session

评论

GitHub Issues · utterances