SwiftUI MapKit 合集

参考:认识 SwiftUI 版 MapKit

Camera 初始化方式

1
2
3
4
5
6
7
8
9
10
11
12
@State private var position = MapCameraPosition.region(
MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 28.209195, longitude: 112.842627), latitudinalMeters: 300, longitudinalMeters: 300)
// MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 28.209195, longitude: 112.842627),
// span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
)

var body: some View {
// Map(initialPosition: position) {
Map(position: $position) {
// initialPosition 与 position 的区别——使用 position 时,更新 MapCameraPosition 会跳转镜头
}
}

其中:

这段代码可以等同于:

1
2
3
4
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 25.7617, longitude: 80.1918),
span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))

Map(coordinateRegion: $region)

地理坐标与墨卡托投影坐标转换

1
2
3
4
5
6
7
8
9
// 经纬度转投影坐标
let coordinate = CLLocationCoordinate2D(latitude: 28.209321, longitude: 112.843033)
let point: MKMapPoint = MKMapPoint(Coordinate)
print(point.x)
print(point.y)

// 投影坐标转经纬度
let point = MKMapPoint(x: 218359192.2732601, y: 112278796.05034925)
let coordinate = point.coordinate

将 iOS 系统自带定位坐标转换成高德坐标

swift Coordinate

系统定位拿到的坐标是 WGS-84 国际标准。

但是在 Map 显示时,因为 iPhone 使用的是高德地图,所以会有偏移,需要把坐标转换为 GCJ-02 火星坐标。

MapControls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Map() {

}
.mapControls({
MapCompass() // 指南针

#if os(watchOS)
MapLocationCompass() // 仅适用于 WatchOS 的指南针
#endif

#if os(macOS)
MapPitchSlider() // 控制地图是否显示高度,也就是 2D / 3D
MapZoomStepper() // 允许用户调节缩放比例
#endif

MapPitchToggle()

MapScaleView(anchorEdge: .trailing) // 用于显示地图比例尺的一个控件。比例尺可以帮助用户了解地图上显示的距离,与实际世界中的距离的比例关系

MapUserLocationButton() // 设置关联的地图位置为当前用户的位置: 右上角的箭头
})

MapStyle

MapStyle有预先配置好的地图样式可以选择,如果没有特别要求,基本可以满足。

  • .standard: 标准样式
  • .imagery: 基于图像的风格,如使用卫星图像的风格。
  • .hybrid: 混合风格,例如使用一个地区的卫星图像,并在其上添加道路和路名信息层。

其次我们可以添加额外配置,控制是否渲染高度(2D, 3D切换)、地图强调风格,显示兴趣点和显示交通情况等。

1
2
3
4
5
6
7
8
9
Map() {

}
.mapStyle(
MapStyle.standard(
elevation: .flat,
emphasis: .muted,
pointsOfInterest: PointOfInterestCategories(arrayLiteral: .publicTransport),
showsTraffic: true))

关于 pointsOfInterest

1
2
.mapStyle(.standard(pointsOfInterest: .excludingAll))					// 除去所有兴趣点
.mapStyle(.standard(pointsOfInterest: .including([.cafe])))

地图交互

Map地图上的交互主要通过配置interactionModes进行设置,有下面几种类型:

  • .all: 允许所有的交互类型
  • .pin: 允许用户平移到地图的不同区域
  • .zoom: 允许用户放大或缩小地图位置
  • .pitch: 允许用户设置地图的间距,从不同的角度查看地图
  • .rotate: 允许用户对地图进行旋转操作
1
2
3
4
Map(
...
interactionModes: [.pitch, .pin],
...){}

MapReader

参考:SwiftUI获取地图信息的MapReader视图

MapReader 是 SwiftUI 提供的一个容器,用于与 Map 视图进行更高级的交互。它允许开发者访问一个 MapProxy 对象,该对象可以提供关于地图当前状态的信息,并支持将屏幕坐标转换为地理坐标等操作。

1
2
3
4
5
6
7
8
9
MapReader { reader in
Map()
.onTapGesture { position in
// 将点击位置的屏幕坐标转换为地理坐标
if let coordinate = reader.convert(position, from: .local) {
print("Tapped at: \(coordinate.latitude), \(coordinate.longitude)")
}
}
}

onTapGesture 不使用 MapReader 时,输出的定位信息是基于屏幕左上角的位置信息,因此不满足在地图中的获取实际的地理位置。

使用 proxy.convert 将点击位置从本地视图坐标(CGPoint)转换为地理坐标(CLLocationCoordinate2D)。

MapReader 需要一个闭包,该闭包接收 reader 对象作为参数,允许开发者通过 reader 访问地图状态或进行相关操作。

坐标转换

convert(_:from:): 将视图坐标转换为地理坐标。

convert(_:to:): 将地理坐标转换为视图坐标。

应用场景

位置标记:允许用户点击地图以标记一个特定的地理位置。

动态状态显示:如显示地图中心点、缩放级别等实时信息。

交互式地图工具:例如,绘制多边形、测量距离、选择特定区域等。

一个 MapKit 没有的功能

iOS 17, SwiftUI MapKit. Has anyone solved for smooth animation of custom annotations?

SwiftUI Map Annotation Coordinate Animation

Map 上的控件在移动时,做不到像 UserAnnotation() 那样顺滑,好像是 withAnimation() 不会生效。


SwiftUI MapKit 合集
https://wonderhoi.com/2024/10/23/SwiftUI-MapKit-合集/
作者
wonderhoi
发布于
2024年10月23日
许可协议