WWDC Quick Look 💓 By SwiftGGTeam
What's new in MapKit

What's new in MapKit

观看原视频

Highlight

MapKit 团队的 Eric 和 Yingxiu 系统介绍了 iOS 16 中 MapKit 的五项重大更新。自从 Apple Maps 推出全新地图和沉浸式 Look Around 体验以来,覆盖范围已经扩展到加拿大、多个欧洲国家和日本,去年还引入了包含车道标线、人行横道、自行车道的 3D 城市体验。今年 MapKit 把这些能力全部开放给了第三方开发者。


核心内容

地图 App 的难点不在“放一张地图”。难点在地图和业务内容如何共存。

租车、导航、旅行、商店定位这类 App,通常会在地图上叠加运营区域、路线、兴趣点和详情卡片。以前这些内容很容易盖住地图标签,或者在 3D 城市里被建筑遮挡。用户看到的是一层业务图形,地图本身的道路、建筑和地形反而退到后面。

02:13)iOS 16 的 MapKit 先降低了升级成本:用新 SDK 重新编译后,iOS、macOS、tvOS App 会自动使用新的 Apple map,并在可用地区显示 3D City Experience。然后,MapKit 增加了配置、覆盖物、混合模式、可选择地图要素和 Look Around 五组 API,让业务内容可以贴着真实地图工作。

这场演讲的示例 App 是一个旧金山 scooter 租赁 App。它有四个功能:运营区域、骑行路线、探索 waterfront 兴趣点、查看景点 Look Around。后面的每个 API,都落在这个具体场景里。


详细内容

用 Map Configuration API 原子化配置地图

02:41)iOS 15 以前,开发者通过 MKMapView 上的多个属性配置地图。iOS 16 软废弃这些分散属性,引入 MKMapConfiguration 作为新的中心入口。

MKMapConfiguration 是抽象基类,下面有三个具体配置:

  • MKImageryMapConfiguration:只显示卫星影像。
  • MKHybridMapConfiguration:显示影像地图,并叠加道路标签和兴趣点等地图要素。
  • MKStandardMapConfiguration:显示完全图形化的标准地图。

这些配置还能控制地形。elevationStyle 可以是 flatrealistic。标准地图还支持 emphasisStyle,其中 muted 会降低地图细节对比度,把注意力让给你自己的图形内容。

// 概念示例:根据 transcript 中出现的配置类和属性整理,省略真实项目中的兼容性判断。
let configuration = MKStandardMapConfiguration()
configuration.elevationStyle = .realistic
configuration.emphasisStyle = .muted
configuration.showsTraffic = true

mapView.preferredConfiguration = configuration

关键点:

  • MKStandardMapConfiguration() 选择图形化标准地图,对应演讲中“fully graphics-based map”。
  • elevationStyle = .realistic 请求真实地形,地图会显示山丘、道路高差和 3D 城市效果。
  • emphasisStyle = .muted 降低地图细节对比,让叠加的业务图形更突出。
  • showsTraffic = true 对应标准地图和混合地图都支持的交通显示开关。
  • preferredConfiguration 把这些选择作为一个配置对象交给 MKMapView,避免多个属性分散修改。

05:42)3D City Experience 有硬件和区域限制。iOS 需要 A12 或更新设备,macOS 需要 M1 或更新 Mac。不可用地区会回退到平面地图;其他设备也会显示平面版本。演讲建议同时测试 3D 和 flat fallback。

覆盖物不再粗暴压住地图

06:55)MapKit 的 overlay 一直有两个层级:aboveRoadsaboveLabelsaboveLabels 会盖在所有内容上,包括标签。Apple 建议只在不希望数据和地图交互时使用它。

iOS 16 把 aboveRoads 作为新的默认层级。它会让覆盖物位于地形、道路、土地覆盖和水体之上,同时保留标签的上下文。

// 概念示例:演讲的 Operating Area 功能把 polygon 作为运营区域叠加到地图上。
let operatingArea = MKPolygon(coordinates: coordinates, count: coordinates.count)
mapView.addOverlay(operatingArea, level: .aboveRoads)

关键点:

  • MKPolygon 对应演讲中“polygon overlay”,用来表示 scooter 的可运营区域。
  • addOverlay 把业务区域加入地图。
  • level: .aboveRoads 选择 iOS 16 推荐层级,覆盖物不会盖住地图标签。
  • 如果选择 aboveLabels,覆盖物会盖住更多地图上下文,只适合少数场景。

08:11)iOS 16 还增加了 transparent buildings。地图有 pitch 时,树和建筑如果挡住 aboveRoads 覆盖物,会自动变透明;pitch 回到 0 度时,冲突的地面对象会从视图中消失,让覆盖物完整可见。

演示里,Yingxiu 把运营区域的 alpha 改成 0.8。这样即使不倾斜地图,用户也能看到下面的道路和建筑。

// 概念示例:renderer 中把 polygon 填充改成半透明。
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    let renderer = MKPolygonRenderer(overlay: overlay)
    renderer.fillColor = UIColor.systemPurple.withAlphaComponent(0.8)
    return renderer
}

关键点:

  • rendererFor overlay 是 MapKit 用来绘制 overlay 的委托入口。
  • MKPolygonRenderer 负责绘制 polygon。
  • withAlphaComponent(0.8) 对应演讲中把 alpha 改成 0.8 的操作。
  • 半透明 overlay 和 transparent buildings 会组合生效,地图仍然保留空间上下文。

路线 overlay 可以跟随真实地形

13:14)普通 overlay 加到 realistic terrain 地图上时,MapKit 会自动把地图切换到 flat。删除最后一个 overlay 后,再恢复 realistic。例外是 Directions API 返回的路线 overlay,它会跟随真实地形。

演示里的 Ride 功能从 Presidio Park entry 到 Battery Spencer,请求一条穿过 Golden Gate Bridge 的路线。路线线条会沿着桥和道路的高差显示,甚至能从桥墩、树木前方透出来。

// 概念示例:演讲用 Directions API 获取路线,并把 route polyline 加到地图。
let source = MKMapItem(placemark: MKPlacemark(coordinate: startCoordinate))
let destination = MKMapItem(placemark: MKPlacemark(coordinate: endCoordinate))

let request = MKDirections.Request()
request.source = source
request.destination = destination

let directions = MKDirections(request: request)
let response = try await directions.calculate()

if let route = response.routes.first {
    mapView.addOverlay(route.polyline, level: .aboveRoads)
}

关键点:

  • MKPlacemark 把起点和终点坐标包装成地点。
  • MKDirections.Request 保存 source 和 destination。
  • MKDirections 请求路线。
  • route.polyline 是 Directions API 返回的路线 overlay。
  • 这类 overlay 是演讲中特别说明的例外:它能保留 realistic terrain,而不是把地图压回 flat。

16:28)elevated route line 支持 A12 及更新 iOS 设备。旧设备会自动得到 2D 地图上的 2D 路线。

用 blendMode 控制 overlay 和地图的视觉关系

17:02)如果目标只是让自定义内容更突出,不一定要把 overlay 放到 aboveLabels。iOS 16 给 MKOverlayRenderer 增加了 blendMode 属性,可以使用 CoreGraphics 的 blend mode。

演讲用 Presidio National Park 做例子:先用一个带 cutout 的大 overlay 覆盖地图区域,再分别设置 hue、hard light、color burn 等混合模式。结果是公园外区域被去饱和并变暗,公园内部仍保持关注度。

// 概念示例:演讲明确说明 MKOverlayRenderer 新增 blendMode 属性。
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    let renderer = MKPolygonRenderer(overlay: overlay)
    renderer.fillColor = UIColor.gray
    renderer.blendMode = .colorBurn
    return renderer
}

关键点:

  • MKOverlayRenderer 是所有 overlay renderer 的基类。
  • blendMode 是 iOS 16 新增属性。
  • .colorBurn 来自 CoreGraphics blend mode,演讲用它得到更克制的 Presidio 高亮效果。
  • overlay 的插入顺序会影响结果:底部 overlay 先和地图混合,上层 overlay 再和前一次结果混合。

让用户直接选择地图上的 POI、区域和自然要素

20:09)过去,用户只能点你自己放到地图上的 annotations。地图里 Apple 提供的商店、餐厅、地标、城市、州、山脉、湖泊等要素只是背景。

iOS 16 的 Selectable Map Features API 改变了这一点。开发者可以配置哪些 map features 可选,然后通过 delegate 接收选择事件。

// 概念示例:演讲的 Explore 功能只启用 points of interest。
mapView.pointOfInterestFilter = MKPointOfInterestFilter(excluding: [
    .airport,
    .carRental,
    .hospital,
    .laundry
])

mapView.selectableMapFeatures = [.pointsOfInterest]

关键点:

  • pointOfInterestFilter 对应演讲中先排除无关 POI 类别的步骤。
  • .airport.carRental.hospital.laundry 是演示中点名排除的类别。
  • selectableMapFeatures 是新的可选地图要素配置入口。
  • 示例只选择 .pointsOfInterest,演讲还提到 territories 和 physical features 也可选。

22:41)用户点击地图要素后,MapKit 会走两个入口。新的 didSelect annotation 回调用来处理选择事件;已有的 viewFor annotation 回调用来定制选中状态的 annotation view。

// 概念示例:演讲通过 MapFeatureAnnotation 定制选中样式,并发起 MKMapItemRequest。
func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
    guard let feature = annotation as? MKMapFeatureAnnotation else { return }

    Task {
        let request = MKMapItemRequest(mapFeatureAnnotation: feature)
        let mapItem = try await request.mapItem
        showInfoCard(for: mapItem)
    }
}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    guard let feature = annotation as? MKMapFeatureAnnotation else { return nil }

    let view = MKMarkerAnnotationView(annotation: feature, reuseIdentifier: "Feature")
    view.markerTintColor = .systemPurple
    view.glyphImage = feature.iconStyle.image
    view.selectedGlyphImage = feature.iconStyle.image
    return view
}

关键点:

  • MKMapFeatureAnnotation 是 iOS 16 新类型,表示用户选中的地图要素。
  • feature.iconStyle 提供 POI 的图标和颜色信息,演讲用它保留 Apple iconography。
  • MKMapItemRequest 根据 map feature 请求更完整的 MKMapItem
  • MKMapItem 可提供地址、名称、电话号码和 URL。
  • viewFor annotation 返回 nil 时,MapKit 使用默认渐变 annotation;返回自定义 view 时,可以套用 App 自己的品牌颜色。

把 Look Around 放进自己的界面

31:14)Look Around 从 iOS 13 开始存在于 Maps app。iOS 16 把它带进 MapKit。开发者可以在自己的 App 中先显示一个小预览,用户点击后进入全屏可交互视图。

使用流程有三步:检查目标位置是否有 Look Around 数据;把 scene 传给 Look Around view controller 或 snapshotter;如果数据可用,就更新 UI 显示预览。

// 概念示例:演讲说明 LookAroundSceneRequest 可用 coordinate 或 map item 初始化。
let request = MKLookAroundSceneRequest(mapItem: mapItem)
let scene = try await request.scene

if let scene {
    lookAroundViewController.scene = scene
    previewContainer.isHidden = false
} else {
    previewContainer.isHidden = true
}

关键点:

  • MKLookAroundSceneRequest 是 iOS 16 新类,用来检查某个位置是否有 Look Around 数据。
  • scene 是 optional async property。可用时返回 scene,不可用时返回 nil,请求出错时抛出错误。
  • Look Around scene 是 opaque object,只作为可用性和显示所需的 token。
  • lookAroundViewController.scene = scene 对应演讲中把 scene 赋给已有 view controller 的做法。
  • previewContainer.isHidden 表示 UI 应根据数据是否可用来显示或隐藏预览。

36:44)Yingxiu 的 Highlights 功能把 MKLookAroundViewController 嵌入底部左侧容器。用户选择 Ferry Building 时,地图先用新 camera API 动画到地点;动画完成后,请求 Look Around scene;有数据就显示预览。Ferry Building 是 landmark,会得到 curated camera framing;Dragon Gate 不是 landmark,所以得到 top-down view。

如果只需要静态图,也可以使用 MKLookAroundSnapshotter 获取 snapshot。

// 概念示例:演讲提到 snapshotter 适合只需要静态图片的场景。
let request = MKLookAroundSceneRequest(coordinate: coordinate)
if let scene = try await request.scene {
    let snapshotter = MKLookAroundSnapshotter(scene: scene)
    let snapshot = try await snapshotter.snapshot
    imageView.image = snapshot.image
}

关键点:

  • MKLookAroundSceneRequest(coordinate:) 使用坐标检查可用性。
  • MKLookAroundSnapshotter 接收 scene。
  • snapshot 是 async property,返回静态预览图。
  • 这种方式适合列表卡片、搜索结果或轻量预览,不需要一开始就嵌入完整交互控制器。

核心启发

1. 做一个城市骑行运营区地图

  • 做什么:在地图上显示可骑行区域、禁停区和推荐停车点。
  • 为什么值得做aboveRoads、transparent buildings 和半透明 polygon 能让运营区域不遮挡道路、标签和建筑。
  • 怎么开始:用 MKPolygon 表示区域,插入到 .aboveRoads,再用 MKPolygonRenderer 设置半透明填充。

2. 做一条带真实高差的城市步行路线

  • 做什么:为桥梁、坡道、公园路线显示贴合地形的路线线。
  • 为什么值得做:Directions API 返回的 route polyline 是普通 overlay 的例外,可以保留 realistic terrain。
  • 怎么开始:用 MKDirections.Request 请求路线,把 route.polyline 加到地图,并在 A12 及更新设备上测试 3D 效果。

3. 做一个可点击的景点探索地图

  • 做什么:用户点击地图上的餐厅、地标、博物馆后,弹出品牌化信息卡。
  • 为什么值得做:Selectable Map Features 让 Apple 地图内置 POI 变成可交互对象,不必自己维护全部 annotation 数据。
  • 怎么开始:配置 selectableMapFeatures = [.pointsOfInterest],在 didSelect 中用 MKMapItemRequest 获取地址、URL 和电话。

4. 做一张带视觉重点的公园或商圈地图

  • 做什么:高亮目标区域,压暗或去饱和周边地图。
  • 为什么值得做MKOverlayRenderer.blendMode 可以控制 overlay 和地图底图的混合方式,比把图层盖在标签上更自然。
  • 怎么开始:准备目标区域 polygon 和外部遮罩 polygon,按顺序插入 overlay,并试验 .hue.hardLight.colorBurn 等 blend mode。

5. 做一个景点 Look Around 预览卡

  • 做什么:用户选中地标后,在地图角落显示 Look Around 小窗口,点击进入全屏街景。
  • 为什么值得做MKLookAroundSceneRequest 可以先判断数据是否存在,避免空白 UI。
  • 怎么开始:用 map item 创建 request,拿到 scene 后赋给 MKLookAroundViewController;没有 scene 时隐藏预览容器。

关联 Session

评论

GitHub Issues · utterances