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.
Files changed (146) hide show
  1. package/README.md +213 -73
  2. package/android/build.gradle +10 -0
  3. package/android/src/main/cpp/CMakeLists.txt +24 -0
  4. package/android/src/main/cpp/cluster_jni.cpp +848 -0
  5. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
  6. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
  7. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
  8. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
  9. package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
  10. package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
  11. package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
  12. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
  13. package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
  14. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
  15. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
  16. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
  17. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
  18. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
  19. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
  20. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
  21. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
  22. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
  23. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
  24. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
  25. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
  26. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
  27. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
  28. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
  29. package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
  30. package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
  31. package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
  32. package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
  33. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
  34. package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
  35. package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
  36. package/build/ExpoGaodeMapNaviView.d.ts +7 -7
  37. package/build/ExpoGaodeMapNaviView.js +10 -11
  38. package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
  39. package/build/index.d.ts +35 -33
  40. package/build/index.js +70 -106
  41. package/build/map/ExpoGaodeMapModule.d.ts +2 -201
  42. package/build/map/ExpoGaodeMapModule.js +586 -18
  43. package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
  44. package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
  45. package/build/map/ExpoGaodeMapView.js +66 -58
  46. package/build/map/components/FoldableMapView.d.ts +38 -0
  47. package/build/map/components/FoldableMapView.js +209 -0
  48. package/build/map/components/MapContext.d.ts +12 -0
  49. package/build/map/components/MapContext.js +54 -0
  50. package/build/map/components/MapUI.d.ts +18 -0
  51. package/build/map/components/MapUI.js +29 -0
  52. package/build/map/components/overlays/Circle.js +34 -3
  53. package/build/map/components/overlays/Cluster.d.ts +3 -1
  54. package/build/map/components/overlays/Cluster.js +31 -2
  55. package/build/map/components/overlays/HeatMap.d.ts +3 -1
  56. package/build/map/components/overlays/HeatMap.js +33 -3
  57. package/build/map/components/overlays/Marker.d.ts +1 -1
  58. package/build/map/components/overlays/Marker.js +37 -32
  59. package/build/map/components/overlays/MultiPoint.js +1 -1
  60. package/build/map/components/overlays/Polygon.js +30 -3
  61. package/build/map/components/overlays/Polyline.js +36 -3
  62. package/build/map/index.d.ts +25 -5
  63. package/build/map/index.js +59 -18
  64. package/build/map/types/common.types.d.ts +40 -0
  65. package/build/map/types/common.types.js +0 -4
  66. package/build/map/types/index.d.ts +3 -2
  67. package/build/map/types/map-view.types.d.ts +108 -3
  68. package/build/map/types/native-module.types.d.ts +363 -0
  69. package/build/map/types/native-module.types.js +5 -0
  70. package/build/map/types/offline.types.d.ts +132 -0
  71. package/build/map/types/offline.types.js +5 -0
  72. package/build/map/types/overlays.types.d.ts +137 -24
  73. package/build/map/utils/ErrorHandler.d.ts +110 -0
  74. package/build/map/utils/ErrorHandler.js +421 -0
  75. package/build/map/utils/GeoUtils.d.ts +20 -0
  76. package/build/map/utils/GeoUtils.js +76 -0
  77. package/build/map/utils/OfflineMapManager.d.ts +148 -0
  78. package/build/map/utils/OfflineMapManager.js +217 -0
  79. package/build/map/utils/PermissionUtils.d.ts +91 -0
  80. package/build/map/utils/PermissionUtils.js +255 -0
  81. package/build/map/utils/PlatformDetector.d.ts +102 -0
  82. package/build/map/utils/PlatformDetector.js +186 -0
  83. package/build/types/index.d.ts +1 -0
  84. package/build/types/index.js +1 -0
  85. package/build/types/native-module.types.d.ts +69 -0
  86. package/build/types/native-module.types.js +2 -0
  87. package/build/types/naviview.types.d.ts +1 -1
  88. package/expo-module.config.json +12 -10
  89. package/ios/ExpoGaodeMapNavigation.podspec +9 -0
  90. package/ios/map/ExpoGaodeMapModule.swift +485 -75
  91. package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
  92. package/ios/map/ExpoGaodeMapView.swift +611 -62
  93. package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
  94. package/ios/map/MapPreloadManager.swift +348 -0
  95. package/ios/map/cpp/ClusterEngine.cpp +110 -0
  96. package/ios/map/cpp/ClusterEngine.hpp +20 -0
  97. package/ios/map/cpp/ColorParser.cpp +135 -0
  98. package/ios/map/cpp/ColorParser.hpp +14 -0
  99. package/ios/map/cpp/GeometryEngine.cpp +574 -0
  100. package/ios/map/cpp/GeometryEngine.hpp +159 -0
  101. package/ios/map/cpp/QuadTree.cpp +92 -0
  102. package/ios/map/cpp/QuadTree.hpp +42 -0
  103. package/ios/map/cpp/README.md +55 -0
  104. package/ios/map/managers/UIManager.swift +72 -1
  105. package/ios/map/modules/LocationManager.swift +123 -166
  106. package/ios/map/overlays/CircleView.swift +16 -32
  107. package/ios/map/overlays/CircleViewModule.swift +12 -12
  108. package/ios/map/overlays/ClusterAnnotation.swift +32 -0
  109. package/ios/map/overlays/ClusterView.swift +331 -45
  110. package/ios/map/overlays/ClusterViewModule.swift +20 -6
  111. package/ios/map/overlays/HeatMapView.swift +135 -32
  112. package/ios/map/overlays/HeatMapViewModule.swift +20 -8
  113. package/ios/map/overlays/MarkerView.swift +613 -130
  114. package/ios/map/overlays/MarkerViewModule.swift +38 -18
  115. package/ios/map/overlays/MultiPointView.swift +168 -10
  116. package/ios/map/overlays/MultiPointViewModule.swift +27 -5
  117. package/ios/map/overlays/PolygonView.swift +62 -23
  118. package/ios/map/overlays/PolygonViewModule.swift +18 -12
  119. package/ios/map/overlays/PolylineView.swift +21 -13
  120. package/ios/map/overlays/PolylineViewModule.swift +18 -12
  121. package/ios/map/utils/ClusterNative.h +96 -0
  122. package/ios/map/utils/ClusterNative.mm +377 -0
  123. package/ios/map/utils/ColorParser.swift +12 -1
  124. package/ios/map/utils/CppBridging.mm +13 -0
  125. package/ios/map/utils/GeometryUtils.swift +34 -0
  126. package/ios/map/utils/LatLngParser.swift +87 -0
  127. package/ios/map/utils/PermissionManager.swift +135 -6
  128. package/package.json +3 -2
  129. package/shared/cpp/ClusterEngine.cpp +110 -0
  130. package/shared/cpp/ClusterEngine.hpp +20 -0
  131. package/shared/cpp/ColorParser.cpp +135 -0
  132. package/shared/cpp/ColorParser.hpp +14 -0
  133. package/shared/cpp/GeometryEngine.cpp +574 -0
  134. package/shared/cpp/GeometryEngine.hpp +159 -0
  135. package/shared/cpp/QuadTree.cpp +92 -0
  136. package/shared/cpp/QuadTree.hpp +42 -0
  137. package/shared/cpp/README.md +55 -0
  138. package/shared/cpp/tests/benchmark_js.js +41 -0
  139. package/shared/cpp/tests/run.sh +17 -0
  140. package/shared/cpp/tests/test_main.cpp +276 -0
  141. package/build/map/ExpoGaodeMap.types.d.ts +0 -41
  142. package/build/map/ExpoGaodeMap.types.js +0 -24
  143. package/build/map/utils/EventManager.d.ts +0 -10
  144. package/build/map/utils/EventManager.js +0 -26
  145. package/build/map/utils/ModuleLoader.d.ts +0 -73
  146. 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 NaviMapView: ExpoView, MAMapViewDelegate {
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
- mapView = MAMapView(frame: bounds)
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. - mapView (可见,在最上层)
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? NaviMarkerView {
213
+ if let markerView = view as? MarkerView {
161
214
  markerView.setMap(mapView)
162
- } else if let circleView = view as? NaviCircleView {
215
+ } else if let circleView = view as? CircleView {
163
216
  circleView.setMap(mapView)
164
- } else if let polylineView = view as? NaviPolylineView {
217
+ } else if let polylineView = view as? PolylineView {
165
218
  polylineView.setMap(mapView)
166
- } else if let polygonView = view as? NaviPolygonView {
219
+ } else if let polygonView = view as? PolygonView {
167
220
  polygonView.setMap(mapView)
168
- } else if let heatMapView = view as? NaviHeatMapView {
221
+ } else if let heatMapView = view as? HeatMapView {
169
222
  heatMapView.setMap(mapView)
170
- } else if let multiPointView = view as? NaviMultiPointView {
223
+ } else if let multiPointView = view as? MultiPointView {
171
224
  multiPointView.setMap(mapView)
172
- } else if let clusterView = view as? NaviClusterView {
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? NaviMarkerView {
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? NaviCircleView {
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? NaviPolylineView {
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? NaviPolygonView {
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? NaviHeatMapView {
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? NaviMultiPointView {
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? NaviClusterView {
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? NaviMarkerView {
254
- if markerView.superview === overlayContainer { return }
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? NaviCircleView {
262
- if circleView.superview === overlayContainer { return }
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? NaviPolylineView {
269
- if polylineView.superview === overlayContainer { return }
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? NaviPolygonView {
276
- if polygonView.superview === overlayContainer { return }
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? NaviHeatMapView {
283
- if heatMapView.superview === overlayContainer { return }
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? NaviMultiPointView {
290
- if multiPointView.superview === overlayContainer { return }
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? NaviClusterView {
297
- if clusterView.superview === overlayContainer { return }
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
- super.willRemoveSubview(subview)
312
-
313
- if let markerView = subview as? NaviMarkerView {
415
+ // 🔑 处理所有覆盖物 - 从跟踪数组中移除并确保 native 对象也从地图移除
416
+ // 🔑 关键修复:先从数组移除,再调用 super,防止 super 触发的事件回调中引用已卸载的视图
417
+ if let markerView = subview as? MarkerView {
314
418
  overlayViews.removeAll { $0 === markerView }
315
- if let annotation = markerView.annotation {
316
- mapView.removeAnnotation(annotation)
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? NaviPolylineView {
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? NaviPolygonView {
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? NaviHeatMapView {
435
+ } else if let heatMapView = subview as? HeatMapView {
334
436
  overlayViews.removeAll { $0 === heatMapView }
335
- } else if let multiPointView = subview as? NaviMultiPointView {
437
+ } else if let multiPointView = subview as? MultiPointView {
336
438
  overlayViews.removeAll { $0 === multiPointView }
337
- } else if let clusterView = subview as? NaviClusterView {
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
- // 先设置 delegate 为 nil,停止接收回调
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 NaviMapView {
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
- onCameraMove([
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
- let circleViews = overlayViews.compactMap { $0 as? NaviCircleView }
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
- let polygonViews = overlayViews.compactMap { $0 as? NaviPolygonView }
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
- let polylineViews = overlayViews.compactMap { $0 as? NaviPolylineView }
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
- let bp = b.distance(from: p)
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? NaviMarkerView, markerView.annotation === annotation {
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? NaviCircleView, let circle = circleView.circle {
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? NaviPolylineView, let polyline = polylineView.polyline, polyline === overlay {
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? NaviPolygonView, let polygon = polygonView.polygon, polygon === overlay {
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? NaviMarkerView {
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? NaviMarkerView, markerView.annotation === annotation {
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
+ }