expo-gaode-map 2.2.30-next.0 → 2.2.30

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 (82) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +4 -2
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +117 -57
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +8 -15
  4. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +20 -6
  5. package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +24 -13
  6. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +351 -0
  7. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +94 -310
  8. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
  9. package/build/ExpoGaodeMapModule.d.ts +13 -5
  10. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  11. package/build/ExpoGaodeMapModule.js +166 -34
  12. package/build/ExpoGaodeMapModule.js.map +1 -1
  13. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  14. package/build/ExpoGaodeMapView.js +12 -0
  15. package/build/ExpoGaodeMapView.js.map +1 -1
  16. package/build/components/AreaMaskOverlay.d.ts +5 -0
  17. package/build/components/AreaMaskOverlay.d.ts.map +1 -0
  18. package/build/components/AreaMaskOverlay.js +20 -0
  19. package/build/components/AreaMaskOverlay.js.map +1 -0
  20. package/build/components/FoldableMapView.d.ts.map +1 -1
  21. package/build/components/FoldableMapView.js +115 -104
  22. package/build/components/FoldableMapView.js.map +1 -1
  23. package/build/components/RouteOverlay.d.ts +5 -0
  24. package/build/components/RouteOverlay.d.ts.map +1 -0
  25. package/build/components/RouteOverlay.js +20 -0
  26. package/build/components/RouteOverlay.js.map +1 -0
  27. package/build/components/overlays/Cluster.d.ts.map +1 -1
  28. package/build/components/overlays/Cluster.js +12 -0
  29. package/build/components/overlays/Cluster.js.map +1 -1
  30. package/build/components/overlays/Marker.d.ts.map +1 -1
  31. package/build/components/overlays/Marker.js +86 -3
  32. package/build/components/overlays/Marker.js.map +1 -1
  33. package/build/hooks/useRoutePlayback.d.ts +4 -0
  34. package/build/hooks/useRoutePlayback.d.ts.map +1 -0
  35. package/build/hooks/useRoutePlayback.js +310 -0
  36. package/build/hooks/useRoutePlayback.js.map +1 -0
  37. package/build/index.d.ts +4 -1
  38. package/build/index.d.ts.map +1 -1
  39. package/build/index.js +4 -2
  40. package/build/index.js.map +1 -1
  41. package/build/types/common.types.d.ts +29 -5
  42. package/build/types/common.types.d.ts.map +1 -1
  43. package/build/types/common.types.js +5 -5
  44. package/build/types/common.types.js.map +1 -1
  45. package/build/types/index.d.ts +3 -2
  46. package/build/types/index.d.ts.map +1 -1
  47. package/build/types/index.js.map +1 -1
  48. package/build/types/location.types.d.ts +28 -0
  49. package/build/types/location.types.d.ts.map +1 -1
  50. package/build/types/location.types.js +5 -0
  51. package/build/types/location.types.js.map +1 -1
  52. package/build/types/map-view.types.d.ts +22 -22
  53. package/build/types/map-view.types.d.ts.map +1 -1
  54. package/build/types/map-view.types.js.map +1 -1
  55. package/build/types/native-module.types.d.ts +2 -2
  56. package/build/types/native-module.types.d.ts.map +1 -1
  57. package/build/types/native-module.types.js.map +1 -1
  58. package/build/types/overlays.types.d.ts +14 -0
  59. package/build/types/overlays.types.d.ts.map +1 -1
  60. package/build/types/overlays.types.js.map +1 -1
  61. package/build/types/route-playback.types.d.ts +118 -0
  62. package/build/types/route-playback.types.d.ts.map +1 -0
  63. package/build/types/route-playback.types.js +2 -0
  64. package/build/types/route-playback.types.js.map +1 -0
  65. package/build/utils/RouteUtils.d.ts +8 -0
  66. package/build/utils/RouteUtils.d.ts.map +1 -0
  67. package/build/utils/RouteUtils.js +140 -0
  68. package/build/utils/RouteUtils.js.map +1 -0
  69. package/ios/ExpoGaodeMapModule.swift +41 -22
  70. package/ios/ExpoGaodeMapView.swift +236 -241
  71. package/ios/ExpoGaodeMapViewModule.swift +16 -11
  72. package/ios/managers/UIManager.swift +5 -4
  73. package/ios/modules/LocationManager.swift +32 -9
  74. package/ios/overlays/ClusterView.swift +114 -12
  75. package/ios/overlays/ClusterViewModule.swift +5 -1
  76. package/ios/overlays/MarkerView.swift +195 -18
  77. package/ios/overlays/MarkerViewModule.swift +7 -7
  78. package/package.json +6 -6
  79. package/build/utils/throttle.d.ts +0 -10
  80. package/build/utils/throttle.d.ts.map +0 -1
  81. package/build/utils/throttle.js +0 -19
  82. package/build/utils/throttle.js.map +0 -1
@@ -2,6 +2,7 @@ import ExpoModulesCore
2
2
  import MAMapKit
3
3
  import MapKit
4
4
  import CoreLocation
5
+ import QuartzCore
5
6
 
6
7
  /**
7
8
  * 高德地图视图组件
@@ -15,8 +16,8 @@ import CoreLocation
15
16
  class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate {
16
17
  // MARK: - 属性
17
18
 
18
- /// 地图类型 (0:标准 1:卫星 2:夜间 3:导航)
19
- var mapType: Int = 0
19
+ /// 地图类型 (1:标准 2:卫星 3:夜间 4:导航 5:公交)
20
+ var mapType: Int = 1
20
21
  /// 初始相机位置
21
22
  var initialCameraPosition: [String: Any]?
22
23
  /// 是否显示缩放控件
@@ -51,6 +52,19 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
51
52
  var showsBuildings: Bool = false
52
53
  /// 是否显示室内地图
53
54
  var showsIndoorMap: Bool = false
55
+ /// 定位最小更新距离
56
+ var distanceFilter: CLLocationDistance = kCLDistanceFilterNone
57
+ /// 朝向最小更新角度
58
+ var headingFilter: CLLocationDegrees = kCLHeadingFilterNone
59
+ /// 相机移动事件节流间隔(毫秒)
60
+ var cameraEventThrottleMs: Int = 32 {
61
+ didSet {
62
+ if cameraEventThrottleMs == 0 {
63
+ cameraMoveDispatchWorkItem?.cancel()
64
+ cameraMoveDispatchWorkItem = nil
65
+ }
66
+ }
67
+ }
54
68
  /// 自定义地图样式配置
55
69
  var customMapStyleData: [String: Any]?
56
70
  /// 是否启用国内外地图自动切换
@@ -59,6 +73,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
59
73
  // MARK: - 事件派发器
60
74
 
61
75
  let onMapPress = EventDispatcher()
76
+ let onPressPoi = EventDispatcher()
62
77
  let onMapLongPress = EventDispatcher()
63
78
  let onLoad = EventDispatcher()
64
79
  let onLocation = EventDispatcher()
@@ -94,6 +109,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
94
109
 
95
110
  /// 缓存的相机移动事件数据
96
111
  private var pendingCameraMoveData: [String: Any]?
112
+ private var cameraMoveDispatchWorkItem: DispatchWorkItem?
113
+ private var lastCameraMoveDispatchTime: CFTimeInterval = 0
97
114
 
98
115
  /// 缩放手势识别器(用于模拟惯性)
99
116
  private var pinchGesture: UIPinchGestureRecognizer!
@@ -185,22 +202,76 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
185
202
 
186
203
  // 统一从 overlayViews 数组设置所有覆盖物(包括 MarkerView)
187
204
  for view in overlayViews {
188
- if let markerView = view as? MarkerView {
189
- markerView.setMap(mapView)
190
- } else if let circleView = view as? CircleView {
191
- circleView.setMap(mapView)
192
- } else if let polylineView = view as? PolylineView {
193
- polylineView.setMap(mapView)
194
- } else if let polygonView = view as? PolygonView {
195
- polygonView.setMap(mapView)
196
- } else if let heatMapView = view as? HeatMapView {
197
- heatMapView.setMap(mapView)
198
- } else if let multiPointView = view as? MultiPointView {
199
- multiPointView.setMap(mapView)
200
- } else if let clusterView = view as? ClusterView {
201
- clusterView.setMap(mapView)
202
- }
205
+ connectOverlayViewToMap(view, mapView: mapView)
206
+ }
207
+ }
208
+
209
+ private func connectOverlayViewToMap(_ view: UIView, mapView: MAMapView) {
210
+ if let markerView = view as? MarkerView {
211
+ markerView.setMap(mapView)
212
+ } else if let circleView = view as? CircleView {
213
+ circleView.setMap(mapView)
214
+ } else if let polylineView = view as? PolylineView {
215
+ polylineView.setMap(mapView)
216
+ } else if let polygonView = view as? PolygonView {
217
+ polygonView.setMap(mapView)
218
+ } else if let heatMapView = view as? HeatMapView {
219
+ heatMapView.setMap(mapView)
220
+ } else if let multiPointView = view as? MultiPointView {
221
+ multiPointView.setMap(mapView)
222
+ } else if let clusterView = view as? ClusterView {
223
+ clusterView.setMap(mapView)
224
+ }
225
+ }
226
+
227
+ private func isOverlayView(_ view: UIView) -> Bool {
228
+ view is MarkerView ||
229
+ view is CircleView ||
230
+ view is PolylineView ||
231
+ view is PolygonView ||
232
+ view is HeatMapView ||
233
+ view is MultiPointView ||
234
+ view is ClusterView
235
+ }
236
+
237
+ private func registerOverlayView(_ view: UIView) {
238
+ guard !overlayViews.contains(where: { $0 === view }) else {
239
+ return
240
+ }
241
+ overlayViews.append(view)
242
+ }
243
+
244
+ private func unregisterOverlayView(_ view: UIView) {
245
+ overlayViews.removeAll { $0 === view }
246
+ }
247
+
248
+ private func prepareOverlayViewForHosting(_ view: UIView) {
249
+ guard !(view is MarkerView) else {
250
+ return
203
251
  }
252
+
253
+ view.alpha = 0
254
+ view.isHidden = true
255
+ }
256
+
257
+ @discardableResult
258
+ private func hostOverlayView(_ view: UIView, moveToOverlayContainer: Bool) -> Bool {
259
+ guard isOverlayView(view) else {
260
+ return false
261
+ }
262
+
263
+ if moveToOverlayContainer {
264
+ overlayContainer.addSubview(view)
265
+ }
266
+
267
+ prepareOverlayViewForHosting(view)
268
+ registerOverlayView(view)
269
+
270
+ if let mapView {
271
+ connectOverlayViewToMap(view, mapView: mapView)
272
+ }
273
+
274
+ return true
204
275
  }
205
276
 
206
277
  /**
@@ -210,77 +281,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
210
281
  override func addSubview(_ view: UIView) {
211
282
  // 🔑 关键修复:旧架构下统一不移动任何覆盖物视图,避免破坏 React Native 布局
212
283
  // 所有覆盖物都隐藏并添加到 overlayViews 数组追踪
213
- if let markerView = view as? MarkerView {
214
- overlayContainer.addSubview(markerView)
215
- // 🔑 关键:MarkerView 不能隐藏,否则 children 无法渲染成图片
216
- // 通过 hitTest 返回 nil 已经确保不阻挡地图交互
217
- overlayViews.append(markerView)
218
- if let mapView {
219
- markerView.setMap(mapView)
220
- }
221
-
222
- return
223
- }
224
-
225
- if let circleView = view as? CircleView {
226
- overlayContainer.addSubview(circleView)
227
- circleView.alpha = 0
228
- circleView.isHidden = true
229
- overlayViews.append(circleView)
230
- if let mapView {
231
- circleView.setMap(mapView)
232
- }
233
-
234
- return
235
- } else if let polylineView = view as? PolylineView {
236
- overlayContainer.addSubview(polylineView)
237
- polylineView.alpha = 0
238
- polylineView.isHidden = true
239
- overlayViews.append(polylineView)
240
- if let mapView {
241
- polylineView.setMap(mapView)
242
- }
243
-
244
- return
245
- } else if let polygonView = view as? PolygonView {
246
- overlayContainer.addSubview(polygonView)
247
- polygonView.alpha = 0
248
- polygonView.isHidden = true
249
- overlayViews.append(polygonView)
250
- if let mapView {
251
- polygonView.setMap(mapView)
252
- }
253
-
254
- return
255
- } else if let heatMapView = view as? HeatMapView {
256
- overlayContainer.addSubview(heatMapView)
257
- heatMapView.alpha = 0
258
- heatMapView.isHidden = true
259
- overlayViews.append(heatMapView)
260
- if let mapView {
261
- heatMapView.setMap(mapView)
262
- }
263
-
264
- return
265
- } else if let multiPointView = view as? MultiPointView {
266
- overlayContainer.addSubview(multiPointView)
267
- multiPointView.alpha = 0
268
- multiPointView.isHidden = true
269
- overlayViews.append(multiPointView)
270
- if let mapView {
271
- multiPointView.setMap(mapView)
272
- }
273
-
274
- return
275
- } else if let clusterView = view as? ClusterView {
276
- overlayContainer.addSubview(clusterView)
277
- clusterView.alpha = 0
278
- clusterView.isHidden = true
279
- overlayViews.append(clusterView)
280
- if let mapView {
281
- clusterView.setMap(mapView)
282
- }
283
-
284
+ if hostOverlayView(view, moveToOverlayContainer: true) {
284
285
  return
285
286
  }
286
287
 
@@ -304,106 +305,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
304
305
  }
305
306
 
306
307
  // 🔑 处理 MarkerView - 新架构下直接连接,旧架构下已在 addSubview 处理
307
- if let markerView = subview as? MarkerView {
308
- // 检查是否已经在容器中(旧架构下 addSubview 已经处理过)
309
- if markerView.superview === overlayContainer {
310
-
311
- return
312
- }
313
-
314
- // 🔑 新架构下也不能隐藏 MarkerView,否则 children 无法渲染
315
- overlayViews.append(markerView)
316
- if let mapView {
317
- markerView.setMap(mapView)
318
- }
319
- // 🔑 关键修复:不再调用 setupAllOverlayViews(),避免所有覆盖物重新设置
320
- return
321
- }
322
-
323
- // 🔑 其他覆盖物不移动视图,只设置连接和隐藏
324
- if let circleView = subview as? CircleView {
325
- if circleView.superview === overlayContainer {
326
-
327
- return
328
- }
329
-
330
- circleView.alpha = 0
331
- circleView.isHidden = true
332
- overlayViews.append(circleView)
333
- if let mapView {
334
- circleView.setMap(mapView)
335
- }
336
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
337
- return
338
- } else if let polylineView = subview as? PolylineView {
339
- if polylineView.superview === overlayContainer {
340
-
341
- return
342
- }
343
-
344
- polylineView.alpha = 0
345
- polylineView.isHidden = true
346
- overlayViews.append(polylineView)
347
- if let mapView {
348
- polylineView.setMap(mapView)
349
- }
350
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
351
- return
352
- } else if let polygonView = subview as? PolygonView {
353
- if polygonView.superview === overlayContainer {
354
-
355
- return
356
- }
357
-
358
- polygonView.alpha = 0
359
- polygonView.isHidden = true
360
- overlayViews.append(polygonView)
361
- if let mapView {
362
- polygonView.setMap(mapView)
363
- }
364
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
365
- return
366
- } else if let heatMapView = subview as? HeatMapView {
367
- if heatMapView.superview === overlayContainer {
368
-
369
- return
370
- }
371
-
372
- heatMapView.alpha = 0
373
- heatMapView.isHidden = true
374
- overlayViews.append(heatMapView)
375
- if let mapView {
376
- heatMapView.setMap(mapView)
377
- }
378
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
379
- return
380
- } else if let multiPointView = subview as? MultiPointView {
381
- if multiPointView.superview === overlayContainer {
382
-
383
- return
384
- }
385
-
386
- multiPointView.alpha = 0
387
- multiPointView.isHidden = true
388
- overlayViews.append(multiPointView)
389
- if let mapView {
390
- multiPointView.setMap(mapView)
391
- }
392
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
393
- return
394
- } else if let clusterView = subview as? ClusterView {
395
- if clusterView.superview === overlayContainer {
396
-
397
- return
398
- }
399
-
400
- clusterView.alpha = 0
401
- clusterView.isHidden = true
402
- overlayViews.append(clusterView)
403
- if let mapView {
404
- clusterView.setMap(mapView)
405
- }
406
- // 🔑 关键修复:不再调用 setupAllOverlayViews()
308
+ if subview.superview !== overlayContainer && hostOverlayView(subview, moveToOverlayContainer: false) {
407
309
  return
408
310
  }
409
311
 
@@ -418,29 +320,29 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
418
320
  // 🔑 处理所有覆盖物 - 从跟踪数组中移除并确保 native 对象也从地图移除
419
321
  // 🔑 关键修复:先从数组移除,再调用 super,防止 super 触发的事件回调中引用已卸载的视图
420
322
  if let markerView = subview as? MarkerView {
421
- overlayViews.removeAll { $0 === markerView }
323
+ unregisterOverlayView(markerView)
422
324
  // MarkerView 内部的 willMove(toSuperview: nil) 会处理 annotation 的移除
423
325
  } else if let circleView = subview as? CircleView {
424
- overlayViews.removeAll { $0 === circleView }
326
+ unregisterOverlayView(circleView)
425
327
  if let mapView, let circle = circleView.circle {
426
328
  mapView.remove(circle)
427
329
  }
428
330
  } else if let polylineView = subview as? PolylineView {
429
- overlayViews.removeAll { $0 === polylineView }
331
+ unregisterOverlayView(polylineView)
430
332
  if let mapView, let polyline = polylineView.polyline {
431
333
  mapView.remove(polyline)
432
334
  }
433
335
  } else if let polygonView = subview as? PolygonView {
434
- overlayViews.removeAll { $0 === polygonView }
336
+ unregisterOverlayView(polygonView)
435
337
  if let mapView, let polygon = polygonView.polygon {
436
338
  mapView.remove(polygon)
437
339
  }
438
340
  } else if let heatMapView = subview as? HeatMapView {
439
- overlayViews.removeAll { $0 === heatMapView }
341
+ unregisterOverlayView(heatMapView)
440
342
  } else if let multiPointView = subview as? MultiPointView {
441
- overlayViews.removeAll { $0 === multiPointView }
343
+ unregisterOverlayView(multiPointView)
442
344
  } else if let clusterView = subview as? ClusterView {
443
- overlayViews.removeAll { $0 === clusterView }
345
+ unregisterOverlayView(clusterView)
444
346
  }
445
347
 
446
348
  super.willRemoveSubview(subview)
@@ -454,7 +356,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
454
356
  return
455
357
  }
456
358
 
457
- uiManager.setMapType(0)
359
+ uiManager.setMapType(1)
458
360
  uiManager.setShowsScale(showsScale)
459
361
  uiManager.setShowsCompass(showsCompass)
460
362
  uiManager.setZoomEnabled(isZoomEnabled)
@@ -462,6 +364,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
462
364
  uiManager.setRotateEnabled(isRotateEnabled)
463
365
  uiManager.setTiltEnabled(isTiltEnabled)
464
366
  uiManager.setShowsUserLocation(showsUserLocation, followUser: followUserLocation)
367
+ updatePinchGestureState()
465
368
  }
466
369
 
467
370
  /**
@@ -492,6 +395,9 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
492
395
  uiManager.setShowsTraffic(showsTraffic)
493
396
  uiManager.setShowsBuildings(showsBuildings)
494
397
  uiManager.setShowsIndoorMap(showsIndoorMap)
398
+ updatePinchGestureState()
399
+ mapView.distanceFilter = distanceFilter
400
+ mapView.headingFilter = headingFilter
495
401
  if let customMapStyleData {
496
402
  uiManager.setCustomMapStyle(customMapStyleData)
497
403
  }
@@ -507,17 +413,20 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
507
413
  */
508
414
  private func updateAppleMapStyle() {
509
415
  switch mapType {
510
- case 1: // 卫星
416
+ case 2: // 卫星
511
417
  appleMapView.mapType = .satellite
512
418
  appleMapView.overrideUserInterfaceStyle = .unspecified
513
- case 2: // 夜间
419
+ case 3: // 夜间
514
420
  // 苹果地图没有专门的夜间模式枚举,通过强制 Dark Mode 实现
515
421
  appleMapView.mapType = .standard
516
422
  appleMapView.overrideUserInterfaceStyle = .dark
517
- case 3: // 导航
423
+ case 4: // 导航
518
424
  appleMapView.mapType = .standard
519
425
  appleMapView.overrideUserInterfaceStyle = .unspecified
520
- default: // 标准 (0)
426
+ case 5: // 公交
427
+ appleMapView.mapType = .standard
428
+ appleMapView.overrideUserInterfaceStyle = .unspecified
429
+ default: // 标准 (1,兼容旧值 0)
521
430
  appleMapView.mapType = .standard
522
431
  // 标准模式下跟随系统,如果系统是深色则显示深色,否则浅色
523
432
  appleMapView.overrideUserInterfaceStyle = .unspecified
@@ -890,7 +799,10 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
890
799
  */
891
800
  deinit {
892
801
  // 清理资源
802
+ stopInertiaAnimation()
893
803
  pendingCameraMoveData = nil
804
+ cameraMoveDispatchWorkItem?.cancel()
805
+ cameraMoveDispatchWorkItem = nil
894
806
  if let privacyObserver {
895
807
  NotificationCenter.default.removeObserver(privacyObserver)
896
808
  }
@@ -940,6 +852,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
940
852
  resolvedMapView.frame = bounds
941
853
  resolvedMapView.delegate = self
942
854
  resolvedMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
855
+ resolvedMapView.touchPOIEnabled = true
943
856
 
944
857
  mapView = resolvedMapView
945
858
  super.addSubview(resolvedMapView)
@@ -961,6 +874,14 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
961
874
  pinchGesture.delegate = self
962
875
  resolvedMapView.addGestureRecognizer(pinchGesture)
963
876
  self.pinchGesture = pinchGesture
877
+ updatePinchGestureState()
878
+ }
879
+
880
+ private func updatePinchGestureState() {
881
+ pinchGesture?.isEnabled = isZoomEnabled
882
+ if !isZoomEnabled {
883
+ stopInertiaAnimation()
884
+ }
964
885
  }
965
886
  }
966
887
 
@@ -1013,42 +934,39 @@ extension ExpoGaodeMapView {
1013
934
  * 地图区域即将改变时触发
1014
935
  */
1015
936
  public func mapView(_ mapView: MAMapView, regionWillChangeAnimated animated: Bool) {
1016
- guard let cameraManager else { return }
937
+ // 保留代理实现,但不在这里分发 onCameraMove。
938
+ // 持续移动过程应使用 mapViewRegionChanged。
939
+ }
1017
940
 
1018
- // 相机开始移动
1019
- let cameraPosition = cameraManager.getCameraPosition()
1020
- let visibleRegion = mapView.region
1021
-
1022
- let eventData: [String: Any] = [
1023
- "cameraPosition": cameraPosition,
1024
- "latLngBounds": [
1025
- "northeast": [
1026
- "latitude": visibleRegion.center.latitude + visibleRegion.span.latitudeDelta / 2,
1027
- "longitude": visibleRegion.center.longitude + visibleRegion.span.longitudeDelta / 2
1028
- ],
1029
- "southwest": [
1030
- "latitude": visibleRegion.center.latitude - visibleRegion.span.latitudeDelta / 2,
1031
- "longitude": visibleRegion.center.longitude - visibleRegion.span.longitudeDelta / 2
1032
- ]
1033
- ]
1034
- ]
1035
-
1036
- // 直接触发事件,移除手动节流
1037
- // 建议在 JS 端进行 debounce/throttle 处理
1038
- onCameraMove(eventData)
941
+ public func mapViewRegionChanged(_ mapView: MAMapView) {
942
+ dispatchCameraMoveEvent(buildCameraEventData(for: mapView))
1039
943
  }
1040
944
 
1041
945
  /**
1042
946
  * 地图区域改变完成后触发
1043
947
  */
1044
948
  public func mapView(_ mapView: MAMapView, regionDidChangeAnimated animated: Bool) {
1045
- guard let cameraManager else { return }
949
+ flushPendingCameraMoveEvent()
950
+ onCameraIdle(buildCameraEventData(for: mapView))
1046
951
 
1047
- // 相机移动完成
1048
- let cameraPosition = cameraManager.getCameraPosition()
1049
- let visibleRegion = mapView.region
952
+ // 这里的 overlayViews 是 [UIView] 类型,可能包含 ClusterView
953
+ for view in overlayViews {
954
+ if let clusterView = view as? ClusterView {
955
+ // 只有当 clusterView 依然在视图树中时才通知
956
+ if clusterView.superview != nil && clusterView.window != nil {
957
+ clusterView.mapRegionDidChange()
958
+ }
959
+ }
960
+ }
1050
961
 
1051
- onCameraIdle([
962
+ handleMapviewRegionChange(mapView: mapView)
963
+ }
964
+
965
+ private func buildCameraEventData(for mapView: MAMapView) -> [String: Any] {
966
+ let cameraPosition = cameraManager?.getCameraPosition() ?? [String: Any]()
967
+ let visibleRegion = mapView.region
968
+
969
+ return [
1052
970
  "cameraPosition": cameraPosition,
1053
971
  "latLngBounds": [
1054
972
  "northeast": [
@@ -1060,25 +978,60 @@ extension ExpoGaodeMapView {
1060
978
  "longitude": visibleRegion.center.longitude - visibleRegion.span.longitudeDelta / 2
1061
979
  ]
1062
980
  ]
1063
- ])
981
+ ]
982
+ }
1064
983
 
1065
- // 这里的 overlayViews [UIView] 类型,可能包含 ClusterView
1066
- for view in overlayViews {
1067
- if let clusterView = view as? ClusterView {
1068
- // 只有当 clusterView 依然在视图树中时才通知
1069
- if clusterView.superview != nil && clusterView.window != nil {
1070
- clusterView.mapRegionDidChange()
1071
- }
1072
- }
984
+ private func dispatchCameraMoveEvent(_ eventData: [String: Any]) {
985
+ let throttleMs = max(cameraEventThrottleMs, 0)
986
+ if throttleMs == 0 {
987
+ pendingCameraMoveData = nil
988
+ cameraMoveDispatchWorkItem?.cancel()
989
+ cameraMoveDispatchWorkItem = nil
990
+ lastCameraMoveDispatchTime = CACurrentMediaTime()
991
+ onCameraMove(eventData)
992
+ return
1073
993
  }
1074
-
1075
- handleMapviewRegionChange(mapView: mapView)
994
+
995
+ pendingCameraMoveData = eventData
996
+
997
+ let now = CACurrentMediaTime()
998
+ let throttleSeconds = Double(throttleMs) / 1000
999
+ let elapsed = now - lastCameraMoveDispatchTime
1000
+
1001
+ if elapsed >= throttleSeconds {
1002
+ cameraMoveDispatchWorkItem?.cancel()
1003
+ cameraMoveDispatchWorkItem = nil
1004
+ flushPendingCameraMoveEvent(at: now)
1005
+ return
1006
+ }
1007
+
1008
+ guard cameraMoveDispatchWorkItem == nil else {
1009
+ return
1010
+ }
1011
+
1012
+ let delay = max(0, throttleSeconds - elapsed)
1013
+ let workItem = DispatchWorkItem { [weak self] in
1014
+ self?.cameraMoveDispatchWorkItem = nil
1015
+ self?.flushPendingCameraMoveEvent()
1016
+ }
1017
+ cameraMoveDispatchWorkItem = workItem
1018
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
1019
+ }
1020
+
1021
+ private func flushPendingCameraMoveEvent(at timestamp: CFTimeInterval = CACurrentMediaTime()) {
1022
+ guard let eventData = pendingCameraMoveData else {
1023
+ return
1024
+ }
1025
+
1026
+ pendingCameraMoveData = nil
1027
+ lastCameraMoveDispatchTime = timestamp
1028
+ onCameraMove(eventData)
1076
1029
  }
1077
1030
 
1078
1031
  /**
1079
1032
  * 地图单击事件
1080
1033
  */
1081
- public func mapView(_ mapView: MAMapView, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
1034
+ private func handleGaodeMapTap(at coordinate: CLLocationCoordinate2D) {
1082
1035
  // 如果正在处理 annotation 选择,跳过地图点击事件
1083
1036
  if isHandlingAnnotationSelect {
1084
1037
  isHandlingAnnotationSelect = false
@@ -1092,6 +1045,15 @@ extension ExpoGaodeMapView {
1092
1045
 
1093
1046
  onMapPress(["latitude": coordinate.latitude, "longitude": coordinate.longitude])
1094
1047
  }
1048
+
1049
+ public func mapView(_ mapView: MAMapView, didSingleTappedAtCoordinate coordinate: CLLocationCoordinate2D) {
1050
+ handleGaodeMapTap(at: coordinate)
1051
+ }
1052
+
1053
+ // 兼容部分旧版 SDK/历史实现
1054
+ public func mapView(_ mapView: MAMapView, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
1055
+ handleGaodeMapTap(at: coordinate)
1056
+ }
1095
1057
 
1096
1058
  /**
1097
1059
  * 检查点击位置是否在圆形内
@@ -1257,9 +1219,37 @@ extension ExpoGaodeMapView {
1257
1219
  /**
1258
1220
  * 地图长按事件
1259
1221
  */
1260
- public func mapView(_ mapView: MAMapView, didLongPressedAt coordinate: CLLocationCoordinate2D) {
1222
+ private func handleGaodeMapLongPress(at coordinate: CLLocationCoordinate2D) {
1261
1223
  onMapLongPress(["latitude": coordinate.latitude, "longitude": coordinate.longitude])
1262
1224
  }
1225
+
1226
+ public func mapView(_ mapView: MAMapView, didLongPressedAtCoordinate coordinate: CLLocationCoordinate2D) {
1227
+ handleGaodeMapLongPress(at: coordinate)
1228
+ }
1229
+
1230
+ // 兼容部分旧版 SDK/历史实现
1231
+ public func mapView(_ mapView: MAMapView, didLongPressedAt coordinate: CLLocationCoordinate2D) {
1232
+ handleGaodeMapLongPress(at: coordinate)
1233
+ }
1234
+
1235
+ private func handleGaodePoiTouch(_ pois: [Any]?) {
1236
+ guard let touchedPoi = pois?.first as? MATouchPoi else {
1237
+ return
1238
+ }
1239
+
1240
+ onPressPoi([
1241
+ "id": touchedPoi.uid ?? "",
1242
+ "name": touchedPoi.name ?? "",
1243
+ "position": [
1244
+ "latitude": touchedPoi.coordinate.latitude,
1245
+ "longitude": touchedPoi.coordinate.longitude
1246
+ ]
1247
+ ])
1248
+ }
1249
+
1250
+ public func mapView(_ mapView: MAMapView!, didTouchPois pois: [Any]!) {
1251
+ handleGaodePoiTouch(pois)
1252
+ }
1263
1253
 
1264
1254
  /**
1265
1255
  * 创建标注视图
@@ -1335,7 +1325,12 @@ extension ExpoGaodeMapView {
1335
1325
  * 标注点击事件
1336
1326
  */
1337
1327
  public func mapView(_ mapView: MAMapView, didSelect view: MAAnnotationView) {
1338
- guard let annotation = view.annotation, !annotation.isKind(of: MAUserLocation.self) else {
1328
+ guard let annotation = view.annotation else {
1329
+ return
1330
+ }
1331
+
1332
+ if annotation.isKind(of: MAUserLocation.self) {
1333
+ mapView.deselectAnnotation(annotation, animated: false)
1339
1334
  return
1340
1335
  }
1341
1336