前言
SwiftUI 与 MapKit 的集成在今年发生了重大变化。在之前的 SwiftUI 版本中,我们将 MKMapView 的基本功能封装到名为 Map 的 SwiftUI 视图中。幸运的是,事情发生了变化,SwiftUI 引入了与 MapKit 集成的新 API。本篇文章我们将学习如何在 SwiftUI 的最新版本中使用可用的新功能丰富的 API 与 MapKit 集成。
正如我之前所说,在 SwiftUI 框架的早期版本中,我们有一个 Map 视图,为我们提供了 MapKit 的基本功能,该功能现在已被弃用。在面向较早 Apple 平台版本的情况下,仍然使用已弃用的 Map 视图是有意义的。
新 MapKit API 的引入
新的 MapKit API 引入了 MapContentBuilder 结果构建器,它看起来类似于 ViewBuilder,但是使用符合 MapContent 协议的类型。让我们从使用 SwiftUI 中最新迭代中提供的新 MapKit API 集成的基本示例开始。
swift
import MapKit
import SwiftUI
extension CLLocationCoordinate2D {
static let newYork: Self = .init(
latitude: 40.730610,
longitude: -73.935242
)
static let seattle: Self = .init(
latitude: 47.608013,
longitude: -122.335167
)
static let sanFrancisco: Self = .init(
latitude: 37.733795,
longitude: -122.446747
)
}
struct ContentView: View {
var body: some View {
Map {
Annotation("Seattle", coordinate: .seattle) {
Image(systemName: "mappin")
.foregroundStyle(.black)
.padding()
.background(.red)
.clipShape(Circle())
}
Marker(coordinate: .newYork) {
Label("New York", systemImage: "mappin")
}
Marker("San Francisco", monogram: Text("SF"), coordinate: .sanFrancisco)
}
}
}
正如你在上面的示例中看到的,我们通过使用 MapContentBuilder 闭包定义地图,并在其上放置内容。MapContentBuilder 类型与符合 MapContent 协议的任何类型一起使用。
在我们的示例中,我们使用了 Marker 和 Annotation 类型。Marker 是一个基本项,允许我们在地图上放置预定义的标记。Annotation 类型更先进,将使我们能够使用纬度和经度在地图上放置 SwiftUI 视图。
SwiftUI 为我们提供了许多符合 MapContent 协议的类型。我们已经使用了其中的两个:Marker 和 Annotation。其中许多包括 MapCircle、MapPolygon、MapPolyline、UserAnnotation 等。
swift
struct ContentView: View {
var body: some View {
Map {
Annotation("Seattle", coordinate: .seattle) {
Image(systemName: "mappin")
.foregroundStyle(.black)
.padding()
.background(.red)
.clipShape(Circle())
}
Marker(coordinate: .newYork) {
Label("New York", systemImage: "mappin")
}
UserAnnotation()
}
}
}
控制初始地图位置
你可以通过使用 Map 初始化器的另一个重载来控制地图的初始位置,该初始化器提供 initialPosition 参数。
swift
struct ContentView: View {
let initialPosition: MapCameraPosition = .userLocation(
fallback: .camera(
MapCamera(centerCoordinate: .newYork, distance: 0)
)
)
var body: some View {
Map(initialPosition: initialPosition) {
Annotation("Seattle", coordinate: .seattle) {
Image(systemName: "mappin")
.foregroundStyle(.black)
.padding()
.background(.red)
.clipShape(Circle())
}
Marker(coordinate: .newYork) {
Label("New York", systemImage: "mappin")
}
Marker("San Francisco", monogram: Text("SF"), coordinate: .sanFrancisco)
}
}
}
initialPosition 参数接受 MapCameraPosition 类型的实例。MapCameraPosition 允许我们以几种方式定义地图位置。它可以是我们在示例中使用的用户位置,或者你可以使用 camera、region、rect 或 item 等静态函数将其指向地图上的任何区域。默认情况下,它使用 MapCameraPosition 类型的自动实例,该类型适合地图内容。
相机位置的双向绑定
每当你需要对相机位置有恒定的控制时,你可以使用 Map 初始化器的另一个重载,允许你提供与地图相机位置的双向绑定。
swift
struct ContentView: View {
@State private var position: MapCameraPosition = .userLocation(
fallback: .camera(
MapCamera(centerCoordinate: .newYork, distance: 0)
)
)
var body: some View {
Map(position: $position) {
// ...
}
}
}
SwiftUI 在用户拖动地图时更新位置绑定。它还在你以编程方式更新 position 属性时立即更新地图相机位置。
swift
struct ContentView: View {
@State private var position: MapCameraPosition = .userLocation(
fallback: .camera(
MapCamera(centerCoordinate: .newYork, distance: 0)
)
)
var body: some View {
Map(position: $position, interactionModes: .pitch) {
// ...
}
}
}
通过使用 interactionModes 参数,你可以控制与地图允许的交互类型。MapInteractionModes 类型定义了一组交互,如平移、俯仰、旋转和缩放。默认情况下,它启用所有可用的交互类型。
总结
今天,我们学习了在 SwiftUI 中集成 MapKit 的基础知识。在接下来的几周里,我们将继续讨论相机操作、地图控件和其他高级主题。希望你喜欢这篇文章。