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

What's new in MapKit

观看原视频

Highlight

MapKit 在 iOS 26 推出 PlaceDescriptor、统一的 MKGeocodingRequest/MKReverseGeocodingRequest、骑行路线 transportType、以及 MapKit JS 的 Look Around 视图。


核心内容

很多 App 的地图体验有一个共同问题:手里只有一段地址或一组经纬度,却拿不到 Apple Maps 那套丰富的店铺数据。过去要么自己拼一个低信息量的标注,要么靠 MKLocalSearch 搜出一堆候选项再筛——CRM 同步、App Intents 之间传值、跨进程引用一个具体地点的场景里都很别扭。

今年 MapKit 引入了 PlaceDescriptor,放在新的 GeoToolbox 框架里。它允许把 commonName、地址、坐标、设备位置打包成一个结构化引用,再交给 MKMapItemRequest 解析成完整的 MKMapItem。解析出来的 MapItem 与通过 Place ID 拿到的一致,能直接显示官方名称、图标、营业时间,也能挂上 Place Card。

地理编码这次也合并了入口。MKGeocodingRequest(地址→坐标)和 MKReverseGeocodingRequest(坐标→地址)取代了 CLGeocoder 在 MapKit 场景下的位置。方向 API 加了 transportType = .cycling,第一次让骑行路径成为一等公民。MapKit JS 这边也补齐了 Look Around 的交互式视图和事件回调,网页端能直接嵌入 3D 街景。


详细内容

PlaceDescriptor 的最小构造只需要一个 representation 加一个 commonName:

// Creating and resolving a PlaceDescriptor with coordinate PlaceRepresentation

import GeoToolbox
import MapKit

let annaLiviaCoordinates = CLLocationCoordinate2D(
    latitude: 53.347673,
    longitude: -6.290198
)
let annaLiviaDescriptor =  PlaceDescriptor(
    representations: [.coordinate(annaLiviaCoordinates)],
    commonName: "Anna Livia Fountain"
)

let request = MKMapItemRequest(placeDescriptor: annaLiviaDescriptor)
do {
    annaLiviaMapItem = try await request.mapItem
} catch {
    print("Error resolving placeDescriptor: \(error)")
}

关键点:

  • import GeoToolbox:PlaceDescriptor 不在 MapKit 主包里,需要单独引入新框架。
  • representations: [.coordinate(...)]:representations 数组按精度降序排列,这里只有一个坐标。
  • commonName:必须是公开知名的名字(如 Sydney Opera House),不要传”妈妈家”这类私有别名,对解析没有帮助。
  • MKMapItemRequest(placeDescriptor:):异步请求 Apple Maps 后端把 descriptor 解析为 MKMapItem。
  • try await request.mapItem:失败时抛出,需要 do/catch 兜底。

如果原始数据是地址而不是坐标,把 representation 换成 .address05:56):

// Creating and resolving a PlaceDescriptor with address PlaceRepresentation

import GeoToolbox
import MapKit

let address = "121-122 James's St, Dublin 8"
let descriptor =  PlaceDescriptor(
    representations: [.address(address)],
    commonName: "Obelisk Fountain"
)

let request = MKMapItemRequest(placeDescriptor: descriptor)
do {
    obeliskFountain = try await request.mapItem
} catch {
    print("Error resolving placeDescriptor: \(error)")
}

关键点:

  • .address(address):使用尽可能完整的地址字符串,包括街号、城市、邮编,命中率更高。
  • representations 数组顺序很重要——如果坐标是从地址反推来的,应把 address 排在前面。
  • 解析逻辑由 MapKit 后端决定,调用方只需提供描述。

跨 App 传递地点时(如 App Intents),可以用 supportingRepresentations 把多家服务的 ID 一起塞进去(06:45):

// Creating a PlaceDescriptor with identifiers

import GeoToolbox

let annaLiviaCoordinates = CLLocationCoordinate2D(
    latitude: 53.347673,
    longitude: -6.290198
)
let identifiers = ["com.apple.MapKit" : "ICBB5FD7684CE949"]
let annaLiviaDescriptor =  PlaceDescriptor(
    representations: [.coordinate(annaLiviaCoordinates)],
    commonName: "Anna Livia Fountain",
    supportingRepresentations: [.serviceIdentifiers(identifiers)]
)

关键点:

  • serviceIdentifiers 是字典:key 是地图服务的 bundle id(com.apple.MapKit),value 是该服务的 place id。
  • 如果有 MapKit 的 identifier,MKMapItemRequest 会优先使用它;解析失败再回退到坐标/地址 representation。
  • 可以并列放多家服务的 id,让接收方根据自己用的 SDK 取对应字段——这就是 PlaceDescriptor 适合做 App Intents 参数的原因。

骑行路线的写法是在原本的 MKDirections.Request 上加一行 transportType16:06):

// Fetch a cycling route

let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.destination = selectedItem
request.transportType = .cycling
let directions = MKDirections(request: request)
do {
    let response = try await directions.calculate()
    returnedRoutes = response.routes
} catch {
    print("Error calculating directions: \(error)")
}

关键点:

  • transportType = .cycling:今年新增的枚举值,之前只支持 walking/automobile/transit。
  • MKMapItem.forCurrentLocation():source 用当前位置时不需要构造坐标。
  • directions.calculate() 仍是异步抛错的接口。
  • 返回的 response.routes 可以直接配 MapPolyline 画在 SwiftUI Map 上。

反向地理编码用 MKReverseGeocodingRequest 替代 CLGeocoder10:45):

// Reverse geocode with MapKit

import MapKit

let millCreekCoordinates = CLLocation(latitude: 39.042617, longitude: -94.587526)
if let request = MKReverseGeocodingRequest(location: millCreekCoordinates) {
    do {
        let mapItems = try await request.mapItems
        millCreekMapItem = mapItems.first
    } catch {
        print("Error reverse geocoding location: \(error)")
    }
}

关键点:

  • 构造器返回 optional,if let 解包失败说明输入坐标不合法。
  • request.mapItems 直接拿 MKMapItem 数组,省掉了 CLPlacemark 到 MKMapItem 的二次转换。
  • 第一个元素通常是最佳匹配,但仍要按业务判断置信度。

核心启发

  1. 把现有的 CRM/Excel 地址数据接入 PlaceDescriptor:很多 App 后端只有名称+地址,过去无法在地图上展示 Apple Maps 风格的标注。为什么值得做:上线后用户在 App 内就能看到官方营业时间、电话、Place Card。怎么开始:把 CLGeocoder.geocodeAddressString 替换为 PlaceDescriptor(representations: [.address(...)], commonName:) + MKMapItemRequest,一处改动覆盖整个数据导入流程。

  2. 给骑行类 App 接 transportType = .cycling:之前要么用步行路径凑合,要么自己接第三方骑行 API。为什么值得做:Apple Maps 的骑行数据覆盖了海拔、坡度、自行车道偏好,原生体验完整。怎么开始:在已有 MKDirections.Request 的代码里加一行 request.transportType = .cycling,先验证路径质量再决定是否暴露给用户做模式切换。

  3. 在 App Intents 入参里使用 PlaceDescriptor:跨 App 传 CLLocation 会丢失店铺名、ID 等语义。为什么值得做:让 Shortcuts、Siri 把”附近的 X 店”这类参数稳定地传给你的 App。怎么开始:把现有 IntentParameter 的 location 字段替换成 PlaceDescriptor,并在 supportingRepresentations 里附带自家服务的 ID。

  4. 在 MapKit JS 中嵌入 Look Around 视图:网页端长期只能展示 2D 地图,街景需要跳到第三方。为什么值得做:旅游/房产网站直接在详情页显示 Apple 的 3D 街景,停留时长会上去。怎么开始:用 mapkit.PlaceLookup 拿到 Place 后构造 mapkit.LookAround(container, place, options),先用 LookAroundPreview 做缩略图占位,点击再展开完整视图。

  5. 统一改写老的 CLGeocoder 调用为 MKGeocodingRequest / MKReverseGeocodingRequest:旧 API 返回 CLPlacemark,需要再转 MKMapItem。为什么值得做:少一层转换,少一处出错点。怎么开始:搜代码库里 CLGeocoder() 的调用点,按地址→MKGeocodingRequest、坐标→MKReverseGeocodingRequest 替换,注意构造器是 optional。


关联 Session

评论

GitHub Issues · utterances