expo-gaode-map-navigation 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -73
- package/android/build.gradle +10 -0
- package/android/src/main/cpp/CMakeLists.txt +24 -0
- package/android/src/main/cpp/cluster_jni.cpp +848 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
- package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
- package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
- package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
- package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
- package/build/ExpoGaodeMapNaviView.d.ts +7 -7
- package/build/ExpoGaodeMapNaviView.js +10 -11
- package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
- package/build/index.d.ts +35 -33
- package/build/index.js +70 -106
- package/build/map/ExpoGaodeMapModule.d.ts +2 -201
- package/build/map/ExpoGaodeMapModule.js +586 -18
- package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
- package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
- package/build/map/ExpoGaodeMapView.js +66 -58
- package/build/map/components/FoldableMapView.d.ts +38 -0
- package/build/map/components/FoldableMapView.js +209 -0
- package/build/map/components/MapContext.d.ts +12 -0
- package/build/map/components/MapContext.js +54 -0
- package/build/map/components/MapUI.d.ts +18 -0
- package/build/map/components/MapUI.js +29 -0
- package/build/map/components/overlays/Circle.js +34 -3
- package/build/map/components/overlays/Cluster.d.ts +3 -1
- package/build/map/components/overlays/Cluster.js +31 -2
- package/build/map/components/overlays/HeatMap.d.ts +3 -1
- package/build/map/components/overlays/HeatMap.js +33 -3
- package/build/map/components/overlays/Marker.d.ts +1 -1
- package/build/map/components/overlays/Marker.js +37 -32
- package/build/map/components/overlays/MultiPoint.js +1 -1
- package/build/map/components/overlays/Polygon.js +30 -3
- package/build/map/components/overlays/Polyline.js +36 -3
- package/build/map/index.d.ts +25 -5
- package/build/map/index.js +59 -18
- package/build/map/types/common.types.d.ts +40 -0
- package/build/map/types/common.types.js +0 -4
- package/build/map/types/index.d.ts +3 -2
- package/build/map/types/map-view.types.d.ts +108 -3
- package/build/map/types/native-module.types.d.ts +363 -0
- package/build/map/types/native-module.types.js +5 -0
- package/build/map/types/offline.types.d.ts +132 -0
- package/build/map/types/offline.types.js +5 -0
- package/build/map/types/overlays.types.d.ts +137 -24
- package/build/map/utils/ErrorHandler.d.ts +110 -0
- package/build/map/utils/ErrorHandler.js +421 -0
- package/build/map/utils/GeoUtils.d.ts +20 -0
- package/build/map/utils/GeoUtils.js +76 -0
- package/build/map/utils/OfflineMapManager.d.ts +148 -0
- package/build/map/utils/OfflineMapManager.js +217 -0
- package/build/map/utils/PermissionUtils.d.ts +91 -0
- package/build/map/utils/PermissionUtils.js +255 -0
- package/build/map/utils/PlatformDetector.d.ts +102 -0
- package/build/map/utils/PlatformDetector.js +186 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/index.js +1 -0
- package/build/types/native-module.types.d.ts +69 -0
- package/build/types/native-module.types.js +2 -0
- package/build/types/naviview.types.d.ts +1 -1
- package/expo-module.config.json +12 -10
- package/ios/ExpoGaodeMapNavigation.podspec +9 -0
- package/ios/map/ExpoGaodeMapModule.swift +485 -75
- package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
- package/ios/map/ExpoGaodeMapView.swift +611 -62
- package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
- package/ios/map/MapPreloadManager.swift +348 -0
- package/ios/map/cpp/ClusterEngine.cpp +110 -0
- package/ios/map/cpp/ClusterEngine.hpp +20 -0
- package/ios/map/cpp/ColorParser.cpp +135 -0
- package/ios/map/cpp/ColorParser.hpp +14 -0
- package/ios/map/cpp/GeometryEngine.cpp +574 -0
- package/ios/map/cpp/GeometryEngine.hpp +159 -0
- package/ios/map/cpp/QuadTree.cpp +92 -0
- package/ios/map/cpp/QuadTree.hpp +42 -0
- package/ios/map/cpp/README.md +55 -0
- package/ios/map/managers/UIManager.swift +72 -1
- package/ios/map/modules/LocationManager.swift +123 -166
- package/ios/map/overlays/CircleView.swift +16 -32
- package/ios/map/overlays/CircleViewModule.swift +12 -12
- package/ios/map/overlays/ClusterAnnotation.swift +32 -0
- package/ios/map/overlays/ClusterView.swift +331 -45
- package/ios/map/overlays/ClusterViewModule.swift +20 -6
- package/ios/map/overlays/HeatMapView.swift +135 -32
- package/ios/map/overlays/HeatMapViewModule.swift +20 -8
- package/ios/map/overlays/MarkerView.swift +613 -130
- package/ios/map/overlays/MarkerViewModule.swift +38 -18
- package/ios/map/overlays/MultiPointView.swift +168 -10
- package/ios/map/overlays/MultiPointViewModule.swift +27 -5
- package/ios/map/overlays/PolygonView.swift +62 -23
- package/ios/map/overlays/PolygonViewModule.swift +18 -12
- package/ios/map/overlays/PolylineView.swift +21 -13
- package/ios/map/overlays/PolylineViewModule.swift +18 -12
- package/ios/map/utils/ClusterNative.h +96 -0
- package/ios/map/utils/ClusterNative.mm +377 -0
- package/ios/map/utils/ColorParser.swift +12 -1
- package/ios/map/utils/CppBridging.mm +13 -0
- package/ios/map/utils/GeometryUtils.swift +34 -0
- package/ios/map/utils/LatLngParser.swift +87 -0
- package/ios/map/utils/PermissionManager.swift +135 -6
- package/package.json +3 -2
- package/shared/cpp/ClusterEngine.cpp +110 -0
- package/shared/cpp/ClusterEngine.hpp +20 -0
- package/shared/cpp/ColorParser.cpp +135 -0
- package/shared/cpp/ColorParser.hpp +14 -0
- package/shared/cpp/GeometryEngine.cpp +574 -0
- package/shared/cpp/GeometryEngine.hpp +159 -0
- package/shared/cpp/QuadTree.cpp +92 -0
- package/shared/cpp/QuadTree.hpp +42 -0
- package/shared/cpp/README.md +55 -0
- package/shared/cpp/tests/benchmark_js.js +41 -0
- package/shared/cpp/tests/run.sh +17 -0
- package/shared/cpp/tests/test_main.cpp +276 -0
- package/build/map/ExpoGaodeMap.types.d.ts +0 -41
- package/build/map/ExpoGaodeMap.types.js +0 -24
- package/build/map/utils/EventManager.d.ts +0 -10
- package/build/map/utils/EventManager.js +0 -26
- package/build/map/utils/ModuleLoader.d.ts +0 -73
- package/build/map/utils/ModuleLoader.js +0 -112
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
2
|
import AMapNaviKit
|
|
3
|
+
import MapKit
|
|
4
|
+
import CoreLocation
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* 高德地图视图组件
|
|
@@ -10,7 +12,7 @@ import AMapNaviKit
|
|
|
10
12
|
* - 覆盖物的添加和管理
|
|
11
13
|
* - 地图事件的派发
|
|
12
14
|
*/
|
|
13
|
-
class
|
|
15
|
+
class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate, MAMultiPointOverlayRendererDelegate {
|
|
14
16
|
// MARK: - 属性
|
|
15
17
|
|
|
16
18
|
/// 地图类型 (0:标准 1:卫星 2:夜间 3:导航)
|
|
@@ -49,6 +51,10 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
49
51
|
var showsBuildings: Bool = false
|
|
50
52
|
/// 是否显示室内地图
|
|
51
53
|
var showsIndoorMap: Bool = false
|
|
54
|
+
/// 自定义地图样式配置
|
|
55
|
+
var customMapStyleData: [String: Any]?
|
|
56
|
+
/// 是否启用国内外地图自动切换
|
|
57
|
+
var enableWorldMapSwitch: Bool = false
|
|
52
58
|
|
|
53
59
|
// MARK: - 事件派发器
|
|
54
60
|
|
|
@@ -63,6 +69,12 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
63
69
|
|
|
64
70
|
/// 高德地图视图实例
|
|
65
71
|
var mapView: MAMapView!
|
|
72
|
+
/// 苹果地图视图实例
|
|
73
|
+
var appleMapView: MKMapView!
|
|
74
|
+
/// 苹果地图代理
|
|
75
|
+
private var appleMapDelegate: AppleMapDelegate!
|
|
76
|
+
/// 是否正在切换地图
|
|
77
|
+
private var isSwitching = false
|
|
66
78
|
/// 相机管理器
|
|
67
79
|
private var cameraManager: CameraManager!
|
|
68
80
|
/// UI 管理器
|
|
@@ -78,6 +90,26 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
78
90
|
/// 显式跟踪所有覆盖物视图(新架构下 subviews 可能不可靠)
|
|
79
91
|
private var overlayViews: [UIView] = []
|
|
80
92
|
|
|
93
|
+
// MARK: - 事件节流控制
|
|
94
|
+
|
|
95
|
+
/// 相机移动事件节流间隔(秒)
|
|
96
|
+
private let cameraMoveThrottleInterval: TimeInterval = 0.1
|
|
97
|
+
/// 上次触发相机移动事件的时间戳
|
|
98
|
+
private var lastCameraMoveTime: TimeInterval = 0
|
|
99
|
+
/// 缓存的相机移动事件数据
|
|
100
|
+
private var pendingCameraMoveData: [String: Any]?
|
|
101
|
+
/// 节流定时器
|
|
102
|
+
private var throttleTimer: Timer?
|
|
103
|
+
|
|
104
|
+
/// 缩放手势识别器(用于模拟惯性)
|
|
105
|
+
private var pinchGesture: UIPinchGestureRecognizer!
|
|
106
|
+
|
|
107
|
+
// 惯性动画相关属性
|
|
108
|
+
private var displayLink: CADisplayLink?
|
|
109
|
+
private var zoomVelocity: Double = 0
|
|
110
|
+
private let friction: Double = 0.92 // 摩擦系数,越接近 1 滑得越远
|
|
111
|
+
private let velocityThreshold: Double = 0.001 // 停止阈值
|
|
112
|
+
|
|
81
113
|
// MARK: - 初始化
|
|
82
114
|
|
|
83
115
|
required init(appContext: AppContext? = nil) {
|
|
@@ -88,10 +120,23 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
88
120
|
MAMapView.updatePrivacyShow(.didShow, privacyInfo: .didContain)
|
|
89
121
|
|
|
90
122
|
// 创建 MAMapView
|
|
91
|
-
|
|
123
|
+
// 尝试从预加载池获取 MapView
|
|
124
|
+
if let preloaded = MapPreloadManager.shared.getPreloadedMapView() {
|
|
125
|
+
mapView = preloaded
|
|
126
|
+
mapView.frame = bounds
|
|
127
|
+
} else {
|
|
128
|
+
mapView = MAMapView(frame: bounds)
|
|
129
|
+
}
|
|
92
130
|
|
|
93
131
|
mapView.delegate = self
|
|
94
132
|
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
133
|
+
|
|
134
|
+
// 创建 MKMapView
|
|
135
|
+
appleMapView = MKMapView(frame: bounds)
|
|
136
|
+
appleMapDelegate = AppleMapDelegate(parent: self)
|
|
137
|
+
appleMapView.delegate = appleMapDelegate
|
|
138
|
+
appleMapView.isHidden = true
|
|
139
|
+
appleMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
95
140
|
|
|
96
141
|
// 创建 MarkerView 隐藏容器
|
|
97
142
|
markerContainer = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
|
|
@@ -109,9 +154,11 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
109
154
|
// 1. self (ExpoGaodeMapView)
|
|
110
155
|
// 2. - markerContainer (隐藏)
|
|
111
156
|
// 3. - overlayContainer (隐藏)
|
|
112
|
-
// 4. -
|
|
157
|
+
// 4. - appleMapView (隐藏)
|
|
158
|
+
// 5. - mapView (可见,在最上层)
|
|
113
159
|
addSubview(markerContainer)
|
|
114
160
|
addSubview(overlayContainer)
|
|
161
|
+
addSubview(appleMapView)
|
|
115
162
|
addSubview(mapView)
|
|
116
163
|
|
|
117
164
|
cameraManager = CameraManager(mapView: mapView)
|
|
@@ -128,11 +175,17 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
128
175
|
}
|
|
129
176
|
|
|
130
177
|
setupDefaultConfig()
|
|
178
|
+
|
|
179
|
+
// 添加 Pinch 手势以支持惯性缩放
|
|
180
|
+
pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
|
|
181
|
+
pinchGesture.delegate = self
|
|
182
|
+
mapView.addGestureRecognizer(pinchGesture)
|
|
131
183
|
}
|
|
132
184
|
|
|
133
185
|
override func layoutSubviews() {
|
|
134
186
|
super.layoutSubviews()
|
|
135
187
|
mapView.frame = bounds
|
|
188
|
+
appleMapView.frame = bounds
|
|
136
189
|
// 🔑 移除自动调用 setupAllOverlayViews(),避免频繁触发
|
|
137
190
|
// layoutSubviews 会在任何视图变化时调用,导致不必要的批量刷新
|
|
138
191
|
}
|
|
@@ -157,19 +210,19 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
157
210
|
private func setupAllOverlayViews() {
|
|
158
211
|
// 统一从 overlayViews 数组设置所有覆盖物(包括 MarkerView)
|
|
159
212
|
for view in overlayViews {
|
|
160
|
-
if let markerView = view as?
|
|
213
|
+
if let markerView = view as? MarkerView {
|
|
161
214
|
markerView.setMap(mapView)
|
|
162
|
-
} else if let circleView = view as?
|
|
215
|
+
} else if let circleView = view as? CircleView {
|
|
163
216
|
circleView.setMap(mapView)
|
|
164
|
-
} else if let polylineView = view as?
|
|
217
|
+
} else if let polylineView = view as? PolylineView {
|
|
165
218
|
polylineView.setMap(mapView)
|
|
166
|
-
} else if let polygonView = view as?
|
|
219
|
+
} else if let polygonView = view as? PolygonView {
|
|
167
220
|
polygonView.setMap(mapView)
|
|
168
|
-
} else if let heatMapView = view as?
|
|
221
|
+
} else if let heatMapView = view as? HeatMapView {
|
|
169
222
|
heatMapView.setMap(mapView)
|
|
170
|
-
} else if let multiPointView = view as?
|
|
223
|
+
} else if let multiPointView = view as? MultiPointView {
|
|
171
224
|
multiPointView.setMap(mapView)
|
|
172
|
-
} else if let clusterView = view as?
|
|
225
|
+
} else if let clusterView = view as? ClusterView {
|
|
173
226
|
clusterView.setMap(mapView)
|
|
174
227
|
}
|
|
175
228
|
}
|
|
@@ -182,54 +235,63 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
182
235
|
override func addSubview(_ view: UIView) {
|
|
183
236
|
// 🔑 关键修复:旧架构下统一不移动任何覆盖物视图,避免破坏 React Native 布局
|
|
184
237
|
// 所有覆盖物都隐藏并添加到 overlayViews 数组追踪
|
|
185
|
-
if let markerView = view as?
|
|
238
|
+
if let markerView = view as? MarkerView {
|
|
186
239
|
overlayContainer.addSubview(markerView)
|
|
240
|
+
// 🔑 关键:MarkerView 不能隐藏,否则 children 无法渲染成图片
|
|
241
|
+
// 通过 hitTest 返回 nil 已经确保不阻挡地图交互
|
|
187
242
|
overlayViews.append(markerView)
|
|
188
243
|
markerView.setMap(mapView)
|
|
244
|
+
|
|
189
245
|
return
|
|
190
246
|
}
|
|
191
247
|
|
|
192
|
-
if let circleView = view as?
|
|
248
|
+
if let circleView = view as? CircleView {
|
|
193
249
|
overlayContainer.addSubview(circleView)
|
|
194
250
|
circleView.alpha = 0
|
|
195
251
|
circleView.isHidden = true
|
|
196
252
|
overlayViews.append(circleView)
|
|
197
253
|
circleView.setMap(mapView)
|
|
254
|
+
|
|
198
255
|
return
|
|
199
|
-
} else if let polylineView = view as?
|
|
256
|
+
} else if let polylineView = view as? PolylineView {
|
|
200
257
|
overlayContainer.addSubview(polylineView)
|
|
201
258
|
polylineView.alpha = 0
|
|
202
259
|
polylineView.isHidden = true
|
|
203
260
|
overlayViews.append(polylineView)
|
|
204
261
|
polylineView.setMap(mapView)
|
|
262
|
+
|
|
205
263
|
return
|
|
206
|
-
} else if let polygonView = view as?
|
|
264
|
+
} else if let polygonView = view as? PolygonView {
|
|
207
265
|
overlayContainer.addSubview(polygonView)
|
|
208
266
|
polygonView.alpha = 0
|
|
209
267
|
polygonView.isHidden = true
|
|
210
268
|
overlayViews.append(polygonView)
|
|
211
269
|
polygonView.setMap(mapView)
|
|
270
|
+
|
|
212
271
|
return
|
|
213
|
-
} else if let heatMapView = view as?
|
|
272
|
+
} else if let heatMapView = view as? HeatMapView {
|
|
214
273
|
overlayContainer.addSubview(heatMapView)
|
|
215
274
|
heatMapView.alpha = 0
|
|
216
275
|
heatMapView.isHidden = true
|
|
217
276
|
overlayViews.append(heatMapView)
|
|
218
277
|
heatMapView.setMap(mapView)
|
|
278
|
+
|
|
219
279
|
return
|
|
220
|
-
} else if let multiPointView = view as?
|
|
280
|
+
} else if let multiPointView = view as? MultiPointView {
|
|
221
281
|
overlayContainer.addSubview(multiPointView)
|
|
222
282
|
multiPointView.alpha = 0
|
|
223
283
|
multiPointView.isHidden = true
|
|
224
284
|
overlayViews.append(multiPointView)
|
|
225
285
|
multiPointView.setMap(mapView)
|
|
286
|
+
|
|
226
287
|
return
|
|
227
|
-
} else if let clusterView = view as?
|
|
288
|
+
} else if let clusterView = view as? ClusterView {
|
|
228
289
|
overlayContainer.addSubview(clusterView)
|
|
229
290
|
clusterView.alpha = 0
|
|
230
291
|
clusterView.isHidden = true
|
|
231
292
|
overlayViews.append(clusterView)
|
|
232
293
|
clusterView.setMap(mapView)
|
|
294
|
+
|
|
233
295
|
return
|
|
234
296
|
}
|
|
235
297
|
|
|
@@ -244,63 +306,105 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
244
306
|
override func didAddSubview(_ subview: UIView) {
|
|
245
307
|
super.didAddSubview(subview)
|
|
246
308
|
|
|
309
|
+
|
|
310
|
+
|
|
247
311
|
// 跳过我们自己创建的容器和地图视图
|
|
248
|
-
if subview === markerContainer || subview === overlayContainer || subview === mapView {
|
|
312
|
+
if subview === markerContainer || subview === overlayContainer || subview === mapView || subview === appleMapView {
|
|
313
|
+
|
|
249
314
|
return
|
|
250
315
|
}
|
|
251
316
|
|
|
252
|
-
// 🔑 处理 MarkerView
|
|
253
|
-
if let markerView = subview as?
|
|
254
|
-
|
|
317
|
+
// 🔑 处理 MarkerView - 新架构下直接连接,旧架构下已在 addSubview 处理
|
|
318
|
+
if let markerView = subview as? MarkerView {
|
|
319
|
+
// 检查是否已经在容器中(旧架构下 addSubview 已经处理过)
|
|
320
|
+
if markerView.superview === overlayContainer {
|
|
321
|
+
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 🔑 新架构下也不能隐藏 MarkerView,否则 children 无法渲染
|
|
255
326
|
overlayViews.append(markerView)
|
|
256
327
|
markerView.setMap(mapView)
|
|
328
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews(),避免所有覆盖物重新设置
|
|
257
329
|
return
|
|
258
330
|
}
|
|
259
331
|
|
|
260
|
-
// 🔑
|
|
261
|
-
if let circleView = subview as?
|
|
262
|
-
if circleView.superview === overlayContainer {
|
|
332
|
+
// 🔑 其他覆盖物不移动视图,只设置连接和隐藏
|
|
333
|
+
if let circleView = subview as? CircleView {
|
|
334
|
+
if circleView.superview === overlayContainer {
|
|
335
|
+
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
263
339
|
circleView.alpha = 0
|
|
264
340
|
circleView.isHidden = true
|
|
265
341
|
overlayViews.append(circleView)
|
|
266
342
|
circleView.setMap(mapView)
|
|
343
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
267
344
|
return
|
|
268
|
-
} else if let polylineView = subview as?
|
|
269
|
-
if polylineView.superview === overlayContainer {
|
|
345
|
+
} else if let polylineView = subview as? PolylineView {
|
|
346
|
+
if polylineView.superview === overlayContainer {
|
|
347
|
+
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
|
|
270
351
|
polylineView.alpha = 0
|
|
271
352
|
polylineView.isHidden = true
|
|
272
353
|
overlayViews.append(polylineView)
|
|
273
354
|
polylineView.setMap(mapView)
|
|
355
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
274
356
|
return
|
|
275
|
-
} else if let polygonView = subview as?
|
|
276
|
-
if polygonView.superview === overlayContainer {
|
|
357
|
+
} else if let polygonView = subview as? PolygonView {
|
|
358
|
+
if polygonView.superview === overlayContainer {
|
|
359
|
+
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
|
|
277
363
|
polygonView.alpha = 0
|
|
278
364
|
polygonView.isHidden = true
|
|
279
365
|
overlayViews.append(polygonView)
|
|
280
366
|
polygonView.setMap(mapView)
|
|
367
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
281
368
|
return
|
|
282
|
-
} else if let heatMapView = subview as?
|
|
283
|
-
if heatMapView.superview === overlayContainer {
|
|
369
|
+
} else if let heatMapView = subview as? HeatMapView {
|
|
370
|
+
if heatMapView.superview === overlayContainer {
|
|
371
|
+
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
|
|
284
375
|
heatMapView.alpha = 0
|
|
285
376
|
heatMapView.isHidden = true
|
|
286
377
|
overlayViews.append(heatMapView)
|
|
287
378
|
heatMapView.setMap(mapView)
|
|
379
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
288
380
|
return
|
|
289
|
-
} else if let multiPointView = subview as?
|
|
290
|
-
if multiPointView.superview === overlayContainer {
|
|
381
|
+
} else if let multiPointView = subview as? MultiPointView {
|
|
382
|
+
if multiPointView.superview === overlayContainer {
|
|
383
|
+
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
|
|
291
387
|
multiPointView.alpha = 0
|
|
292
388
|
multiPointView.isHidden = true
|
|
293
389
|
overlayViews.append(multiPointView)
|
|
294
390
|
multiPointView.setMap(mapView)
|
|
391
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
295
392
|
return
|
|
296
|
-
} else if let clusterView = subview as?
|
|
297
|
-
if clusterView.superview === overlayContainer {
|
|
393
|
+
} else if let clusterView = subview as? ClusterView {
|
|
394
|
+
if clusterView.superview === overlayContainer {
|
|
395
|
+
|
|
396
|
+
return
|
|
397
|
+
}
|
|
398
|
+
|
|
298
399
|
clusterView.alpha = 0
|
|
299
400
|
clusterView.isHidden = true
|
|
300
401
|
overlayViews.append(clusterView)
|
|
301
402
|
clusterView.setMap(mapView)
|
|
403
|
+
// 🔑 关键修复:不再调用 setupAllOverlayViews()
|
|
302
404
|
return
|
|
303
405
|
}
|
|
406
|
+
|
|
407
|
+
|
|
304
408
|
}
|
|
305
409
|
|
|
306
410
|
/**
|
|
@@ -308,35 +412,35 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
308
412
|
* 新架构下需要手动清理 overlayViews 数组和地图覆盖物
|
|
309
413
|
*/
|
|
310
414
|
override func willRemoveSubview(_ subview: UIView) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if let markerView = subview as?
|
|
415
|
+
// 🔑 处理所有覆盖物 - 从跟踪数组中移除并确保 native 对象也从地图移除
|
|
416
|
+
// 🔑 关键修复:先从数组移除,再调用 super,防止 super 触发的事件回调中引用已卸载的视图
|
|
417
|
+
if let markerView = subview as? MarkerView {
|
|
314
418
|
overlayViews.removeAll { $0 === markerView }
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
} else if let circleView = subview as? NaviCircleView {
|
|
419
|
+
// MarkerView 内部的 willMove(toSuperview: nil) 会处理 annotation 的移除
|
|
420
|
+
} else if let circleView = subview as? CircleView {
|
|
319
421
|
overlayViews.removeAll { $0 === circleView }
|
|
320
422
|
if let circle = circleView.circle {
|
|
321
423
|
mapView.remove(circle)
|
|
322
424
|
}
|
|
323
|
-
} else if let polylineView = subview as?
|
|
425
|
+
} else if let polylineView = subview as? PolylineView {
|
|
324
426
|
overlayViews.removeAll { $0 === polylineView }
|
|
325
427
|
if let polyline = polylineView.polyline {
|
|
326
428
|
mapView.remove(polyline)
|
|
327
429
|
}
|
|
328
|
-
} else if let polygonView = subview as?
|
|
430
|
+
} else if let polygonView = subview as? PolygonView {
|
|
329
431
|
overlayViews.removeAll { $0 === polygonView }
|
|
330
432
|
if let polygon = polygonView.polygon {
|
|
331
433
|
mapView.remove(polygon)
|
|
332
434
|
}
|
|
333
|
-
} else if let heatMapView = subview as?
|
|
435
|
+
} else if let heatMapView = subview as? HeatMapView {
|
|
334
436
|
overlayViews.removeAll { $0 === heatMapView }
|
|
335
|
-
} else if let multiPointView = subview as?
|
|
437
|
+
} else if let multiPointView = subview as? MultiPointView {
|
|
336
438
|
overlayViews.removeAll { $0 === multiPointView }
|
|
337
|
-
} else if let clusterView = subview as?
|
|
439
|
+
} else if let clusterView = subview as? ClusterView {
|
|
338
440
|
overlayViews.removeAll { $0 === clusterView }
|
|
339
441
|
}
|
|
442
|
+
|
|
443
|
+
super.willRemoveSubview(subview)
|
|
340
444
|
}
|
|
341
445
|
|
|
342
446
|
/**
|
|
@@ -376,9 +480,96 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
376
480
|
uiManager.setShowsBuildings(showsBuildings)
|
|
377
481
|
uiManager.setShowsIndoorMap(showsIndoorMap)
|
|
378
482
|
|
|
483
|
+
// 更新苹果地图样式
|
|
484
|
+
updateAppleMapStyle()
|
|
485
|
+
|
|
379
486
|
// applyProps 时不再需要手动收集视图,因为 addSubview 已经处理了
|
|
380
487
|
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 更新苹果地图样式以匹配高德地图设置
|
|
491
|
+
*/
|
|
492
|
+
private func updateAppleMapStyle() {
|
|
493
|
+
switch mapType {
|
|
494
|
+
case 1: // 卫星
|
|
495
|
+
appleMapView.mapType = .satellite
|
|
496
|
+
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
497
|
+
case 2: // 夜间
|
|
498
|
+
// 苹果地图没有专门的夜间模式枚举,通过强制 Dark Mode 实现
|
|
499
|
+
appleMapView.mapType = .standard
|
|
500
|
+
appleMapView.overrideUserInterfaceStyle = .dark
|
|
501
|
+
case 3: // 导航
|
|
502
|
+
appleMapView.mapType = .standard
|
|
503
|
+
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
504
|
+
default: // 标准 (0)
|
|
505
|
+
appleMapView.mapType = .standard
|
|
506
|
+
// 标准模式下跟随系统,如果系统是深色则显示深色,否则浅色
|
|
507
|
+
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// MARK: - 手势处理
|
|
381
512
|
|
|
513
|
+
@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
|
|
514
|
+
if gesture.state == .began {
|
|
515
|
+
// 手势开始,立即停止之前的惯性动画,避免冲突
|
|
516
|
+
stopInertiaAnimation()
|
|
517
|
+
} else if gesture.state == .ended || gesture.state == .cancelled {
|
|
518
|
+
let velocity = gesture.velocity
|
|
519
|
+
|
|
520
|
+
// 只有速度足够大才触发惯性
|
|
521
|
+
// 阈值过滤,避免轻微操作触发滑动
|
|
522
|
+
if abs(velocity) > 0.1 {
|
|
523
|
+
// 转换速度:scale/s -> zoomLevel/frame
|
|
524
|
+
// 0.02 是经验系数,用于将手势速度映射到每帧的 zoomLevel 增量
|
|
525
|
+
zoomVelocity = Double(velocity) * 0.02
|
|
526
|
+
startInertiaAnimation()
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private func startInertiaAnimation() {
|
|
532
|
+
stopInertiaAnimation()
|
|
533
|
+
displayLink = CADisplayLink(target: self, selector: #selector(updateInertia))
|
|
534
|
+
displayLink?.add(to: .main, forMode: .common)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private func stopInertiaAnimation() {
|
|
538
|
+
displayLink?.invalidate()
|
|
539
|
+
displayLink = nil
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
@objc private func updateInertia() {
|
|
543
|
+
// 应用速度
|
|
544
|
+
var newZoom = mapView.zoomLevel + zoomVelocity
|
|
545
|
+
|
|
546
|
+
// 边界检查
|
|
547
|
+
if newZoom < mapView.minZoomLevel || newZoom > mapView.maxZoomLevel {
|
|
548
|
+
// 碰到边界,停止动画
|
|
549
|
+
newZoom = max(mapView.minZoomLevel, min(mapView.maxZoomLevel, newZoom))
|
|
550
|
+
stopInertiaAnimation()
|
|
551
|
+
mapView.setZoomLevel(newZoom, animated: false)
|
|
552
|
+
return
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// 更新地图缩放级别(animated: false 以保证逐帧控制的流畅性)
|
|
556
|
+
mapView.setZoomLevel(newZoom, animated: false)
|
|
557
|
+
|
|
558
|
+
// 减速(应用摩擦力)
|
|
559
|
+
zoomVelocity *= friction
|
|
560
|
+
|
|
561
|
+
// 停止条件
|
|
562
|
+
if abs(zoomVelocity) < velocityThreshold {
|
|
563
|
+
stopInertiaAnimation()
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// UIGestureRecognizerDelegate
|
|
568
|
+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
569
|
+
// 允许我们的 Pinch 手势与地图内部的手势同时识别
|
|
570
|
+
return true
|
|
571
|
+
}
|
|
572
|
+
|
|
382
573
|
// MARK: - 缩放控制
|
|
383
574
|
|
|
384
575
|
func setMaxZoom(_ maxZoom: Double) {
|
|
@@ -429,6 +620,18 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
429
620
|
uiManager.setShowsIndoorMap(show)
|
|
430
621
|
}
|
|
431
622
|
|
|
623
|
+
/**
|
|
624
|
+
* 设置自定义地图样式
|
|
625
|
+
* @param styleData 样式配置
|
|
626
|
+
*/
|
|
627
|
+
func setCustomMapStyle(_ styleData: [String: Any]) {
|
|
628
|
+
customMapStyleData = styleData
|
|
629
|
+
// 如果地图已加载,立即应用样式
|
|
630
|
+
if isMapLoaded {
|
|
631
|
+
uiManager.setCustomMapStyle(styleData)
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
432
635
|
func setFollowUserLocation(_ follow: Bool) {
|
|
433
636
|
followUserLocation = follow
|
|
434
637
|
uiManager.setShowsUserLocation(showsUserLocation, followUser: follow)
|
|
@@ -459,36 +662,288 @@ class NaviMapView: ExpoView, MAMapViewDelegate {
|
|
|
459
662
|
|
|
460
663
|
|
|
461
664
|
|
|
665
|
+
// MARK: - 地图切换逻辑
|
|
666
|
+
|
|
667
|
+
func handleMapviewRegionChange(mapView: UIView) {
|
|
668
|
+
if !enableWorldMapSwitch {
|
|
669
|
+
return
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if mapView.isHidden {
|
|
673
|
+
return
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if isSwitching {
|
|
677
|
+
isSwitching = false
|
|
678
|
+
return
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if mapView.isKind(of: MAMapView.self) {
|
|
682
|
+
if !AMapDataAvailableForCoordinate(self.mapView.centerCoordinate) {
|
|
683
|
+
showSwitchAlert(message: "是否切换到苹果地图显示", toApple: true)
|
|
684
|
+
}
|
|
685
|
+
} else if mapView.isKind(of: MKMapView.self) {
|
|
686
|
+
if AMapDataAvailableForCoordinate(self.appleMapView.centerCoordinate) {
|
|
687
|
+
showSwitchAlert(message: "是否切换到高德地图显示", toApple: false)
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
func showSwitchAlert(message: String, toApple: Bool) {
|
|
693
|
+
// Find top controller
|
|
694
|
+
guard let controller = self.findViewController() else { return }
|
|
695
|
+
|
|
696
|
+
// Check if alert is already presented to avoid stacking
|
|
697
|
+
if controller.presentedViewController is UIAlertController {
|
|
698
|
+
return
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
let alert = UIAlertController(title: "", message: message, preferredStyle: .alert)
|
|
702
|
+
alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: nil))
|
|
703
|
+
alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { _ in
|
|
704
|
+
self.performSwitching()
|
|
705
|
+
}))
|
|
706
|
+
controller.present(alert, animated: true, completion: nil)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
func performSwitching() {
|
|
710
|
+
|
|
711
|
+
self.isSwitching = true
|
|
712
|
+
|
|
713
|
+
let isGaodeCurrentlyVisible = !self.mapView.isHidden
|
|
714
|
+
|
|
715
|
+
self.mapView.isHidden = isGaodeCurrentlyVisible
|
|
716
|
+
self.appleMapView.isHidden = !isGaodeCurrentlyVisible
|
|
717
|
+
|
|
718
|
+
if !isGaodeCurrentlyVisible {
|
|
719
|
+
// 切换到高德 (Apple -> Gaode)
|
|
720
|
+
let region = self.MARegionForMKRegion(mkRegion: self.appleMapView.region)
|
|
721
|
+
// 简单的合法性检查
|
|
722
|
+
if region.span.latitudeDelta > 0 && region.span.longitudeDelta > 0 {
|
|
723
|
+
self.mapView.region = region
|
|
724
|
+
}
|
|
725
|
+
self.mapView.centerCoordinate = self.appleMapView.centerCoordinate
|
|
726
|
+
self.mapView.rotationDegree = CGFloat(self.appleMapView.camera.heading)
|
|
727
|
+
} else {
|
|
728
|
+
// 切换到苹果 (Gaode -> Apple)
|
|
729
|
+
let gaodeRegion = self.mapView.region
|
|
730
|
+
let gaodeCenter = self.mapView.centerCoordinate
|
|
731
|
+
let gaodeHeading = self.mapView.rotationDegree
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
// 1. 设置 Region
|
|
735
|
+
let mkRegion = self.MKRegionForMARegion(maRegion: gaodeRegion)
|
|
736
|
+
// 确保 span 有效
|
|
737
|
+
if mkRegion.span.latitudeDelta > 0 && mkRegion.span.longitudeDelta > 0 {
|
|
738
|
+
self.appleMapView.setRegion(mkRegion, animated: false)
|
|
739
|
+
} else {
|
|
740
|
+
// 如果 span 无效,至少设置中心点
|
|
741
|
+
self.appleMapView.setCenter(gaodeCenter, animated: false)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// 2. 尝试同步 Heading (可选,如果导致问题可先注释)
|
|
745
|
+
// 注意:直接修改 camera.heading 可能无效或导致问题,建议使用 setCamera
|
|
746
|
+
let currentCamera = self.appleMapView.camera
|
|
747
|
+
// 调试:打印 altitude
|
|
748
|
+
|
|
749
|
+
// 如果 altitude 为 0,通常意味着地图还没完全初始化好。
|
|
750
|
+
// 此时可以尝试给一个默认的高度,或者仅仅 setRegion 就够了。
|
|
751
|
+
// 经验值:如果不设置 altitude,有时视角会极低导致看起来像黑屏。
|
|
752
|
+
let altitudeToUse = currentCamera.altitude > 0 ? currentCamera.altitude : 10000.0 // 给个默认高度 10000米
|
|
753
|
+
|
|
754
|
+
let newCamera = MKMapCamera(lookingAtCenter: gaodeCenter, fromDistance: altitudeToUse, pitch: currentCamera.pitch, heading: CLLocationDirection(gaodeHeading))
|
|
755
|
+
self.appleMapView.setCamera(newCamera, animated: false)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// 强制布局更新,确保 Frame 正确
|
|
759
|
+
self.setNeedsLayout()
|
|
760
|
+
self.layoutIfNeeded()
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
func MARegionForMKRegion(mkRegion: MKCoordinateRegion) -> MACoordinateRegion {
|
|
764
|
+
return MACoordinateRegion(center: mkRegion.center, span: MACoordinateSpan(latitudeDelta: mkRegion.span.latitudeDelta, longitudeDelta: mkRegion.span.longitudeDelta))
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
func MKRegionForMARegion(maRegion: MACoordinateRegion) -> MKCoordinateRegion {
|
|
768
|
+
return MKCoordinateRegion(center: maRegion.center, span: MKCoordinateSpan(latitudeDelta: maRegion.span.latitudeDelta, longitudeDelta: maRegion.span.longitudeDelta))
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// MARK: - 截图
|
|
772
|
+
|
|
773
|
+
func takeSnapshot(completion: @escaping (String?, Error?) -> Void) {
|
|
774
|
+
if !appleMapView.isHidden {
|
|
775
|
+
// 苹果地图
|
|
776
|
+
UIGraphicsBeginImageContextWithOptions(bounds.size, true, UIScreen.main.scale)
|
|
777
|
+
|
|
778
|
+
if let superview = self.superview {
|
|
779
|
+
// 如果有父视图(通常是 React Native 的容器),直接绘制父视图
|
|
780
|
+
superview.drawHierarchy(in: bounds, afterScreenUpdates: true)
|
|
781
|
+
} else {
|
|
782
|
+
// 降级方案:只绘制自己
|
|
783
|
+
drawHierarchy(in: bounds, afterScreenUpdates: true)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
let image = UIGraphicsGetImageFromCurrentImageContext()
|
|
787
|
+
UIGraphicsEndImageContext()
|
|
788
|
+
|
|
789
|
+
saveSnapshot(image: image, completion: completion)
|
|
790
|
+
return
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// 高德地图:使用新的异步 API (takeSnapshotInRect:withCompletionBlock:)
|
|
794
|
+
mapView.takeSnapshot(in: bounds) { [weak self] (image, state) in
|
|
795
|
+
guard let self = self else { return }
|
|
796
|
+
|
|
797
|
+
// 检查截图是否成功
|
|
798
|
+
guard let mapImage = image else {
|
|
799
|
+
completion(nil, NSError(domain: "ExpoGaodeMap", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to take map snapshot"]))
|
|
800
|
+
return
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// 开始绘制合成图
|
|
804
|
+
// 🔑 将 opaque 设为 false,避免透明背景的 UI 组件在绘制时变黑
|
|
805
|
+
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale)
|
|
806
|
+
|
|
807
|
+
// 1. 绘制底图
|
|
808
|
+
mapImage.draw(in: self.bounds)
|
|
809
|
+
|
|
810
|
+
// 2. 绘制上层 UI 子视图 (React Native 的 UI 组件)
|
|
811
|
+
if let superview = self.superview {
|
|
812
|
+
for subview in superview.subviews {
|
|
813
|
+
// 跳过自己(ExpoGaodeMapView),因为已经画了底图
|
|
814
|
+
if subview != self && !subview.isHidden {
|
|
815
|
+
// 绘制兄弟节点
|
|
816
|
+
subview.drawHierarchy(in: subview.frame, afterScreenUpdates: true)
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
} else {
|
|
820
|
+
// 如果没有 superview(不太可能),回退到只绘制自己的子视图
|
|
821
|
+
for subview in self.subviews {
|
|
822
|
+
if subview != self.mapView && subview != self.appleMapView && !subview.isHidden {
|
|
823
|
+
subview.drawHierarchy(in: subview.frame, afterScreenUpdates: true)
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
|
|
829
|
+
UIGraphicsEndImageContext()
|
|
830
|
+
|
|
831
|
+
self.saveSnapshot(image: finalImage, completion: completion)
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
private func saveSnapshot(image: UIImage?, completion: @escaping (String?, Error?) -> Void) {
|
|
836
|
+
guard let finalImage = image,
|
|
837
|
+
let data = finalImage.pngData() else {
|
|
838
|
+
completion(nil, NSError(domain: "ExpoGaodeMap", code: -2, userInfo: [NSLocalizedDescriptionKey: "Failed to generate PNG data"]))
|
|
839
|
+
return
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
let filename = UUID().uuidString + ".png"
|
|
843
|
+
let tempDirectory = FileManager.default.temporaryDirectory
|
|
844
|
+
let fileURL = tempDirectory.appendingPathComponent(filename)
|
|
845
|
+
|
|
846
|
+
do {
|
|
847
|
+
try data.write(to: fileURL)
|
|
848
|
+
completion(fileURL.absoluteString, nil)
|
|
849
|
+
} catch {
|
|
850
|
+
completion(nil, error)
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
462
854
|
/**
|
|
463
855
|
* 析构函数 - 清理资源
|
|
856
|
+
* 当视图从层级中移除并释放时自动调用
|
|
464
857
|
*/
|
|
465
858
|
deinit {
|
|
466
|
-
//
|
|
859
|
+
// 清理节流定时器
|
|
860
|
+
throttleTimer?.invalidate()
|
|
861
|
+
throttleTimer = nil
|
|
862
|
+
pendingCameraMoveData = nil
|
|
863
|
+
|
|
864
|
+
// 清理代理,停止接收回调
|
|
467
865
|
mapView?.delegate = nil
|
|
866
|
+
appleMapView?.delegate = nil
|
|
867
|
+
appleMapDelegate = nil
|
|
868
|
+
|
|
869
|
+
// 清除所有覆盖物和标注
|
|
870
|
+
mapView?.removeAnnotations(mapView?.annotations ?? [])
|
|
871
|
+
mapView?.removeOverlays(mapView?.overlays ?? [])
|
|
872
|
+
appleMapView?.removeAnnotations(appleMapView?.annotations ?? [])
|
|
873
|
+
appleMapView?.removeOverlays(appleMapView?.overlays ?? [])
|
|
874
|
+
|
|
875
|
+
// 清空覆盖物数组
|
|
876
|
+
overlayViews.removeAll()
|
|
877
|
+
|
|
878
|
+
// 移除所有子视图
|
|
879
|
+
markerContainer?.removeFromSuperview()
|
|
880
|
+
overlayContainer?.removeFromSuperview()
|
|
881
|
+
mapView?.removeFromSuperview()
|
|
882
|
+
appleMapView?.removeFromSuperview()
|
|
883
|
+
|
|
884
|
+
// 释放引用
|
|
885
|
+
mapView = nil
|
|
886
|
+
appleMapView = nil
|
|
887
|
+
cameraManager = nil
|
|
888
|
+
uiManager = nil
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// MARK: - AppleMapDelegate
|
|
893
|
+
|
|
894
|
+
class AppleMapDelegate: NSObject, MKMapViewDelegate {
|
|
895
|
+
weak var parent: ExpoGaodeMapView?
|
|
896
|
+
|
|
897
|
+
init(parent: ExpoGaodeMapView) {
|
|
898
|
+
self.parent = parent
|
|
899
|
+
super.init()
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
|
|
903
|
+
parent?.handleMapviewRegionChange(mapView: mapView)
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
extension UIView {
|
|
908
|
+
func findViewController() -> UIViewController? {
|
|
909
|
+
if let nextResponder = self.next as? UIViewController {
|
|
910
|
+
return nextResponder
|
|
911
|
+
} else if let nextResponder = self.next as? UIView {
|
|
912
|
+
return nextResponder.findViewController()
|
|
913
|
+
} else {
|
|
914
|
+
return nil
|
|
915
|
+
}
|
|
468
916
|
}
|
|
469
917
|
}
|
|
470
918
|
|
|
471
919
|
// MARK: - MAMapViewDelegate
|
|
472
920
|
|
|
473
|
-
extension
|
|
921
|
+
extension ExpoGaodeMapView {
|
|
474
922
|
/**
|
|
475
923
|
* 地图加载完成回调
|
|
476
924
|
*/
|
|
477
925
|
public func mapViewDidFinishLoadingMap(_ mapView: MAMapView) {
|
|
478
926
|
guard !isMapLoaded else { return }
|
|
479
927
|
isMapLoaded = true
|
|
928
|
+
|
|
929
|
+
// 地图加载完成后,应用自定义样式
|
|
930
|
+
if let styleData = customMapStyleData {
|
|
931
|
+
uiManager.setCustomMapStyle(styleData)
|
|
932
|
+
}
|
|
933
|
+
|
|
480
934
|
onLoad(["loaded": true])
|
|
481
935
|
}
|
|
482
936
|
|
|
483
937
|
/**
|
|
484
|
-
* 地图区域即将改变时触发
|
|
938
|
+
* 地图区域即将改变时触发 - 应用节流优化
|
|
485
939
|
*/
|
|
486
940
|
public func mapView(_ mapView: MAMapView, regionWillChangeAnimated animated: Bool) {
|
|
487
|
-
// 相机开始移动
|
|
941
|
+
// 相机开始移动 - 应用节流优化
|
|
942
|
+
let currentTime = Date().timeIntervalSince1970
|
|
488
943
|
let cameraPosition = cameraManager.getCameraPosition()
|
|
489
944
|
let visibleRegion = mapView.region
|
|
490
945
|
|
|
491
|
-
|
|
946
|
+
let eventData: [String: Any] = [
|
|
492
947
|
"cameraPosition": cameraPosition,
|
|
493
948
|
"latLngBounds": [
|
|
494
949
|
"northeast": [
|
|
@@ -500,7 +955,31 @@ extension NaviMapView {
|
|
|
500
955
|
"longitude": visibleRegion.center.longitude - visibleRegion.span.longitudeDelta / 2
|
|
501
956
|
]
|
|
502
957
|
]
|
|
503
|
-
]
|
|
958
|
+
]
|
|
959
|
+
|
|
960
|
+
// 节流逻辑:0.1秒 内只触发一次
|
|
961
|
+
if currentTime - lastCameraMoveTime >= cameraMoveThrottleInterval {
|
|
962
|
+
// 超过节流时间,立即触发事件
|
|
963
|
+
lastCameraMoveTime = currentTime
|
|
964
|
+
onCameraMove(eventData)
|
|
965
|
+
// 清除待处理的事件和定时器
|
|
966
|
+
throttleTimer?.invalidate()
|
|
967
|
+
throttleTimer = nil
|
|
968
|
+
pendingCameraMoveData = nil
|
|
969
|
+
} else {
|
|
970
|
+
// 在节流时间内,缓存事件数据,使用定时器延迟触发
|
|
971
|
+
pendingCameraMoveData = eventData
|
|
972
|
+
throttleTimer?.invalidate()
|
|
973
|
+
|
|
974
|
+
let delay = cameraMoveThrottleInterval - (currentTime - lastCameraMoveTime)
|
|
975
|
+
throttleTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self] _ in
|
|
976
|
+
guard let self = self, let data = self.pendingCameraMoveData else { return }
|
|
977
|
+
self.lastCameraMoveTime = Date().timeIntervalSince1970
|
|
978
|
+
self.onCameraMove(data)
|
|
979
|
+
self.pendingCameraMoveData = nil
|
|
980
|
+
self.throttleTimer = nil
|
|
981
|
+
}
|
|
982
|
+
}
|
|
504
983
|
}
|
|
505
984
|
|
|
506
985
|
/**
|
|
@@ -524,6 +1003,18 @@ extension NaviMapView {
|
|
|
524
1003
|
]
|
|
525
1004
|
]
|
|
526
1005
|
])
|
|
1006
|
+
|
|
1007
|
+
// 这里的 overlayViews 是 [UIView] 类型,可能包含 ClusterView
|
|
1008
|
+
for view in overlayViews {
|
|
1009
|
+
if let clusterView = view as? ClusterView {
|
|
1010
|
+
// 只有当 clusterView 依然在视图树中时才通知
|
|
1011
|
+
if clusterView.superview != nil && clusterView.window != nil {
|
|
1012
|
+
clusterView.mapRegionDidChange()
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
handleMapviewRegionChange(mapView: mapView)
|
|
527
1018
|
}
|
|
528
1019
|
|
|
529
1020
|
/**
|
|
@@ -548,7 +1039,8 @@ extension NaviMapView {
|
|
|
548
1039
|
* 检查点击位置是否在圆形内
|
|
549
1040
|
*/
|
|
550
1041
|
private func checkCirclePress(at coordinate: CLLocationCoordinate2D) -> Bool {
|
|
551
|
-
|
|
1042
|
+
// 从 overlayViews 数组中查找 CircleView
|
|
1043
|
+
let circleViews = overlayViews.compactMap { $0 as? CircleView }
|
|
552
1044
|
|
|
553
1045
|
for circleView in circleViews {
|
|
554
1046
|
guard let circle = circleView.circle else {
|
|
@@ -583,7 +1075,8 @@ extension NaviMapView {
|
|
|
583
1075
|
* 检查点击位置是否在多边形内
|
|
584
1076
|
*/
|
|
585
1077
|
private func checkPolygonPress(at coordinate: CLLocationCoordinate2D) -> Bool {
|
|
586
|
-
|
|
1078
|
+
// 从 overlayViews 数组中查找 PolygonView
|
|
1079
|
+
let polygonViews = overlayViews.compactMap { $0 as? PolygonView }
|
|
587
1080
|
|
|
588
1081
|
for polygonView in polygonViews {
|
|
589
1082
|
guard let polygon = polygonView.polygon else {
|
|
@@ -606,7 +1099,8 @@ extension NaviMapView {
|
|
|
606
1099
|
* 检查点击位置是否在折线附近
|
|
607
1100
|
*/
|
|
608
1101
|
private func checkPolylinePress(at coordinate: CLLocationCoordinate2D) -> Bool {
|
|
609
|
-
|
|
1102
|
+
// 从 overlayViews 数组中查找 PolylineView
|
|
1103
|
+
let polylineViews = overlayViews.compactMap { $0 as? PolylineView }
|
|
610
1104
|
let threshold: Double = 20.0 // 20米容差
|
|
611
1105
|
|
|
612
1106
|
for polylineView in polylineViews {
|
|
@@ -686,7 +1180,7 @@ extension NaviMapView {
|
|
|
686
1180
|
|
|
687
1181
|
let ab = a.distance(from: b)
|
|
688
1182
|
let ap = a.distance(from: p)
|
|
689
|
-
|
|
1183
|
+
_ = b.distance(from: p)
|
|
690
1184
|
|
|
691
1185
|
if ab == 0 { return ap }
|
|
692
1186
|
|
|
@@ -718,13 +1212,38 @@ extension NaviMapView {
|
|
|
718
1212
|
return nil
|
|
719
1213
|
}
|
|
720
1214
|
|
|
1215
|
+
// 🔑 支持 MAAnimatedAnnotation(平滑移动)
|
|
1216
|
+
if annotation.isKind(of: MAAnimatedAnnotation.self) {
|
|
1217
|
+
// 从 overlayViews 数组查找对应的 MarkerView
|
|
1218
|
+
for view in overlayViews {
|
|
1219
|
+
if let markerView = view as? MarkerView,
|
|
1220
|
+
let animatedAnnotation = markerView.animatedAnnotation,
|
|
1221
|
+
animatedAnnotation === annotation {
|
|
1222
|
+
return markerView.getAnimatedAnnotationView(for: mapView, annotation: annotation)
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return nil
|
|
1226
|
+
}
|
|
1227
|
+
|
|
721
1228
|
if annotation.isKind(of: MAPointAnnotation.self) {
|
|
1229
|
+
// 🔑 统一从 overlayViews 数组查找 MarkerView(新旧架构统一)
|
|
722
1230
|
for view in overlayViews {
|
|
723
|
-
if let markerView = view as?
|
|
1231
|
+
if let markerView = view as? MarkerView, markerView.annotation === annotation {
|
|
724
1232
|
return markerView.getAnnotationView(for: mapView, annotation: annotation)
|
|
725
1233
|
}
|
|
726
1234
|
}
|
|
727
1235
|
}
|
|
1236
|
+
|
|
1237
|
+
// 🔑 支持 ClusterAnnotation
|
|
1238
|
+
if annotation.isKind(of: ClusterAnnotation.self) {
|
|
1239
|
+
for view in overlayViews {
|
|
1240
|
+
if let clusterView = view as? ClusterView,
|
|
1241
|
+
let annotationView = clusterView.viewForAnnotation(annotation) {
|
|
1242
|
+
return annotationView
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
728
1247
|
return nil
|
|
729
1248
|
}
|
|
730
1249
|
|
|
@@ -733,17 +1252,24 @@ extension NaviMapView {
|
|
|
733
1252
|
* 从 overlayContainer 中查找对应的视图
|
|
734
1253
|
*/
|
|
735
1254
|
public func mapView(_ mapView: MAMapView, rendererFor overlay: MAOverlay) -> MAOverlayRenderer {
|
|
1255
|
+
// 从 overlayViews 数组中查找
|
|
736
1256
|
for view in overlayViews {
|
|
737
|
-
if let circleView = view as?
|
|
1257
|
+
if let circleView = view as? CircleView, let circle = circleView.circle {
|
|
738
1258
|
if circle === overlay {
|
|
739
1259
|
return circleView.getRenderer()
|
|
740
1260
|
}
|
|
741
|
-
} else if let polylineView = view as?
|
|
1261
|
+
} else if let polylineView = view as? PolylineView, let polyline = polylineView.polyline, polyline === overlay {
|
|
742
1262
|
return polylineView.getRenderer()
|
|
743
|
-
} else if let polygonView = view as?
|
|
1263
|
+
} else if let polygonView = view as? PolygonView, let polygon = polygonView.polygon, polygon === overlay {
|
|
744
1264
|
return polygonView.getRenderer()
|
|
1265
|
+
} else if let heatMapView = view as? HeatMapView, let heatmap = heatMapView.heatmapOverlay, heatmap === overlay {
|
|
1266
|
+
return heatMapView.getRenderer()
|
|
1267
|
+
} else if let multiPointView = view as? MultiPointView, let renderer = multiPointView.getRenderer(), renderer.overlay === overlay {
|
|
1268
|
+
renderer.delegate = self
|
|
1269
|
+
return renderer
|
|
745
1270
|
}
|
|
746
1271
|
}
|
|
1272
|
+
|
|
747
1273
|
return MAOverlayRenderer(overlay: overlay)
|
|
748
1274
|
}
|
|
749
1275
|
|
|
@@ -758,8 +1284,9 @@ extension NaviMapView {
|
|
|
758
1284
|
// 标记正在处理 annotation 选择,阻止地图点击事件
|
|
759
1285
|
isHandlingAnnotationSelect = true
|
|
760
1286
|
|
|
1287
|
+
// 🔑 统一从 overlayViews 查找 MarkerView(新旧架构统一)
|
|
761
1288
|
for view in overlayViews {
|
|
762
|
-
if let markerView = view as?
|
|
1289
|
+
if let markerView = view as? MarkerView {
|
|
763
1290
|
if markerView.annotation === annotation {
|
|
764
1291
|
let eventData: [String: Any] = [
|
|
765
1292
|
"latitude": annotation.coordinate.latitude,
|
|
@@ -768,6 +1295,11 @@ extension NaviMapView {
|
|
|
768
1295
|
markerView.onMarkerPress(eventData)
|
|
769
1296
|
return
|
|
770
1297
|
}
|
|
1298
|
+
} else if let clusterView = view as? ClusterView {
|
|
1299
|
+
if clusterView.containsAnnotation(annotation) {
|
|
1300
|
+
clusterView.handleAnnotationTap(annotation)
|
|
1301
|
+
return
|
|
1302
|
+
}
|
|
771
1303
|
}
|
|
772
1304
|
}
|
|
773
1305
|
|
|
@@ -789,8 +1321,9 @@ extension NaviMapView {
|
|
|
789
1321
|
"longitude": coord.longitude
|
|
790
1322
|
]
|
|
791
1323
|
|
|
1324
|
+
// 🔑 统一从 overlayViews 查找 MarkerView(新旧架构统一)
|
|
792
1325
|
for view in overlayViews {
|
|
793
|
-
if let markerView = view as?
|
|
1326
|
+
if let markerView = view as? MarkerView, markerView.annotation === annotation {
|
|
794
1327
|
switch newState {
|
|
795
1328
|
case .starting:
|
|
796
1329
|
markerView.onMarkerDragStart(event)
|
|
@@ -807,3 +1340,19 @@ extension NaviMapView {
|
|
|
807
1340
|
|
|
808
1341
|
}
|
|
809
1342
|
}
|
|
1343
|
+
|
|
1344
|
+
// MARK: - MAMultiPointOverlayRendererDelegate
|
|
1345
|
+
|
|
1346
|
+
extension ExpoGaodeMapView {
|
|
1347
|
+
public func multiPointOverlayRenderer(_ renderer: MAMultiPointOverlayRenderer!, didItemTapped item: MAMultiPointItem!) {
|
|
1348
|
+
// 查找对应的 MultiPointView
|
|
1349
|
+
for view in overlayViews {
|
|
1350
|
+
if let multiPointView = view as? MultiPointView,
|
|
1351
|
+
let r = multiPointView.getRenderer(),
|
|
1352
|
+
r === renderer {
|
|
1353
|
+
multiPointView.handleMultiPointClick(item: item)
|
|
1354
|
+
return
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|