expo-gaode-map 1.1.7 → 2.0.0-alpha.1

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 (121) hide show
  1. package/README.en.md +32 -46
  2. package/README.md +51 -71
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +37 -237
  4. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +1 -49
  5. package/android/src/main/java/expo/modules/gaodemap/managers/CameraManager.kt +30 -7
  6. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +1 -0
  7. package/android/src/main/java/expo/modules/gaodemap/modules/LocationManager.kt +10 -1
  8. package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +38 -14
  9. package/android/src/main/java/expo/modules/gaodemap/overlays/CircleViewModule.kt +3 -3
  10. package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +8 -1
  11. package/android/src/main/java/expo/modules/gaodemap/overlays/HeatMapView.kt +4 -1
  12. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +322 -93
  13. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +11 -3
  14. package/android/src/main/java/expo/modules/gaodemap/overlays/MultiPointView.kt +4 -1
  15. package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonView.kt +25 -11
  16. package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonViewModule.kt +3 -3
  17. package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineView.kt +20 -10
  18. package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineViewModule.kt +6 -2
  19. package/build/ExpoGaodeMap.types.d.ts +27 -6
  20. package/build/ExpoGaodeMap.types.d.ts.map +1 -1
  21. package/build/ExpoGaodeMap.types.js +3 -0
  22. package/build/ExpoGaodeMap.types.js.map +1 -1
  23. package/build/ExpoGaodeMapModule.d.ts +157 -10
  24. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  25. package/build/ExpoGaodeMapModule.js +4 -0
  26. package/build/ExpoGaodeMapModule.js.map +1 -1
  27. package/build/ExpoGaodeMapView.d.ts +1 -17
  28. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  29. package/build/ExpoGaodeMapView.js +4 -209
  30. package/build/ExpoGaodeMapView.js.map +1 -1
  31. package/build/components/overlays/Circle.d.ts +10 -1
  32. package/build/components/overlays/Circle.d.ts.map +1 -1
  33. package/build/components/overlays/Circle.js +11 -86
  34. package/build/components/overlays/Circle.js.map +1 -1
  35. package/build/components/overlays/Cluster.d.ts.map +1 -1
  36. package/build/components/overlays/Cluster.js.map +1 -1
  37. package/build/components/overlays/Marker.d.ts +13 -1
  38. package/build/components/overlays/Marker.d.ts.map +1 -1
  39. package/build/components/overlays/Marker.js +51 -115
  40. package/build/components/overlays/Marker.js.map +1 -1
  41. package/build/components/overlays/Polygon.d.ts +7 -15
  42. package/build/components/overlays/Polygon.d.ts.map +1 -1
  43. package/build/components/overlays/Polygon.js +10 -80
  44. package/build/components/overlays/Polygon.js.map +1 -1
  45. package/build/components/overlays/Polyline.d.ts +7 -14
  46. package/build/components/overlays/Polyline.d.ts.map +1 -1
  47. package/build/components/overlays/Polyline.js +9 -66
  48. package/build/components/overlays/Polyline.js.map +1 -1
  49. package/build/index.d.ts +1 -4
  50. package/build/index.d.ts.map +1 -1
  51. package/build/index.js +2 -10
  52. package/build/index.js.map +1 -1
  53. package/build/types/index.d.ts +1 -1
  54. package/build/types/index.d.ts.map +1 -1
  55. package/build/types/index.js.map +1 -1
  56. package/build/types/map-view.types.d.ts +0 -76
  57. package/build/types/map-view.types.d.ts.map +1 -1
  58. package/build/types/map-view.types.js.map +1 -1
  59. package/build/types/overlays.types.d.ts +11 -16
  60. package/build/types/overlays.types.d.ts.map +1 -1
  61. package/build/types/overlays.types.js.map +1 -1
  62. package/docs/API.en.md +1 -21
  63. package/docs/API.md +84 -56
  64. package/docs/EXAMPLES.en.md +0 -48
  65. package/docs/EXAMPLES.md +49 -102
  66. package/docs/INITIALIZATION.md +59 -71
  67. package/docs/MIGRATION.md +423 -0
  68. package/ios/ExpoGaodeMapView.swift +317 -258
  69. package/ios/ExpoGaodeMapViewModule.swift +3 -50
  70. package/ios/managers/CameraManager.swift +23 -2
  71. package/ios/managers/UIManager.swift +10 -5
  72. package/ios/modules/LocationManager.swift +10 -0
  73. package/ios/overlays/CircleView.swift +98 -19
  74. package/ios/overlays/CircleViewModule.swift +21 -0
  75. package/ios/overlays/ClusterView.swift +33 -4
  76. package/ios/overlays/HeatMapView.swift +16 -4
  77. package/ios/overlays/MarkerView.swift +235 -146
  78. package/ios/overlays/MarkerViewModule.swift +7 -3
  79. package/ios/overlays/MultiPointView.swift +30 -1
  80. package/ios/overlays/PolygonView.swift +63 -12
  81. package/ios/overlays/PolygonViewModule.swift +17 -0
  82. package/ios/overlays/PolylineView.swift +95 -25
  83. package/ios/overlays/PolylineViewModule.swift +17 -8
  84. package/ios/utils/PermissionManager.swift +9 -14
  85. package/package.json +4 -3
  86. package/src/ExpoGaodeMap.types.ts +28 -3
  87. package/src/ExpoGaodeMapModule.ts +201 -12
  88. package/src/ExpoGaodeMapView.tsx +11 -225
  89. package/src/components/overlays/Circle.tsx +12 -104
  90. package/src/components/overlays/Cluster.tsx +0 -1
  91. package/src/components/overlays/Marker.tsx +63 -138
  92. package/src/components/overlays/Polygon.tsx +12 -92
  93. package/src/components/overlays/Polyline.tsx +11 -77
  94. package/src/index.ts +4 -29
  95. package/src/types/index.ts +1 -1
  96. package/src/types/map-view.types.ts +1 -69
  97. package/src/types/overlays.types.ts +11 -16
  98. package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +0 -574
  99. package/build/modules/AMapLocation.d.ts +0 -78
  100. package/build/modules/AMapLocation.d.ts.map +0 -1
  101. package/build/modules/AMapLocation.js +0 -132
  102. package/build/modules/AMapLocation.js.map +0 -1
  103. package/build/modules/AMapPermissions.d.ts +0 -29
  104. package/build/modules/AMapPermissions.d.ts.map +0 -1
  105. package/build/modules/AMapPermissions.js +0 -23
  106. package/build/modules/AMapPermissions.js.map +0 -1
  107. package/build/modules/AMapSDK.d.ts +0 -22
  108. package/build/modules/AMapSDK.d.ts.map +0 -1
  109. package/build/modules/AMapSDK.js +0 -25
  110. package/build/modules/AMapSDK.js.map +0 -1
  111. package/build/modules/AMapView.d.ts +0 -44
  112. package/build/modules/AMapView.d.ts.map +0 -1
  113. package/build/modules/AMapView.js +0 -65
  114. package/build/modules/AMapView.js.map +0 -1
  115. package/ios/managers/OverlayManager.swift +0 -522
  116. package/src/modules/AMapLocation.ts +0 -165
  117. package/src/modules/AMapPermissions.ts +0 -41
  118. package/src/modules/AMapSDK.ts +0 -31
  119. package/src/modules/AMapView.ts +0 -72
  120. package/test/ClockMapView.tsx +0 -532
  121. package/test/useMap.ts +0 -1360
@@ -9,7 +9,7 @@ public class ExpoGaodeMapViewModule: Module {
9
9
  Name("ExpoGaodeMapView")
10
10
 
11
11
  View(ExpoGaodeMapView.self) {
12
- Events("onMapPress", "onMapLongPress", "onLoad", "onLocation", "onMarkerPress", "onMarkerDragStart", "onMarkerDrag", "onMarkerDragEnd", "onCirclePress", "onPolygonPress", "onPolylinePress")
12
+ Events("onMapPress", "onMapLongPress", "onLoad", "onLocation")
13
13
 
14
14
  Prop("mapType") { (view: ExpoGaodeMapView, type: Int) in
15
15
  view.mapType = type
@@ -102,54 +102,7 @@ public class ExpoGaodeMapViewModule: Module {
102
102
  AsyncFunction("getCameraPosition") { (view: ExpoGaodeMapView) -> [String: Any] in
103
103
  return view.getCameraPosition()
104
104
  }
105
-
106
- AsyncFunction("addCircle") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
107
- view.addCircle(id: id, props: props)
108
- }
109
-
110
- AsyncFunction("removeCircle") { (view: ExpoGaodeMapView, id: String) in
111
- view.removeCircle(id: id)
112
- }
113
-
114
- AsyncFunction("updateCircle") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
115
- view.updateCircle(id: id, props: props)
116
- }
117
-
118
- AsyncFunction("addMarker") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
119
- view.addMarker(id: id, props: props)
120
- }
121
-
122
- AsyncFunction("removeMarker") { (view: ExpoGaodeMapView, id: String) in
123
- view.removeMarker(id: id)
124
- }
125
-
126
- AsyncFunction("updateMarker") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
127
- view.updateMarker(id: id, props: props)
128
- }
129
-
130
- AsyncFunction("addPolyline") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
131
- view.addPolyline(id: id, props: props)
132
- }
133
-
134
- AsyncFunction("removePolyline") { (view: ExpoGaodeMapView, id: String) in
135
- view.removePolyline(id: id)
136
- }
137
-
138
- AsyncFunction("updatePolyline") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
139
- view.updatePolyline(id: id, props: props)
140
- }
141
-
142
- AsyncFunction("addPolygon") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
143
- view.addPolygon(id: id, props: props)
144
- }
145
-
146
- AsyncFunction("removePolygon") { (view: ExpoGaodeMapView, id: String) in
147
- view.removePolygon(id: id)
148
- }
149
-
150
- AsyncFunction("updatePolygon") { (view: ExpoGaodeMapView, id: String, props: [String: Any]) in
151
- view.updatePolygon(id: id, props: props)
152
- }
105
+
153
106
  }
154
107
  }
155
- }
108
+ }
@@ -49,11 +49,18 @@ class CameraManager {
49
49
  if let target = position["target"] as? [String: Double],
50
50
  let latitude = target["latitude"],
51
51
  let longitude = target["longitude"] {
52
+ // 🔑 坐标验证
53
+ guard latitude >= -90 && latitude <= 90,
54
+ longitude >= -180 && longitude <= 180 else {
55
+ return
56
+ }
52
57
  status.centerCoordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
53
58
  }
54
59
 
55
60
  if let zoom = position["zoom"] as? Double {
56
- status.zoomLevel = CGFloat(zoom)
61
+ // 🔑 缩放级别范围限制 (3-20)
62
+ let validZoom = max(3, min(20, zoom))
63
+ status.zoomLevel = CGFloat(validZoom)
57
64
  }
58
65
 
59
66
  if let bearing = position["bearing"] as? Double {
@@ -82,6 +89,11 @@ class CameraManager {
82
89
  if let target = position["target"] as? [String: Double],
83
90
  let latitude = target["latitude"],
84
91
  let longitude = target["longitude"] {
92
+ // 🔑 坐标验证
93
+ guard latitude >= -90 && latitude <= 90,
94
+ longitude >= -180 && longitude <= 180 else {
95
+ return
96
+ }
85
97
  status.centerCoordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
86
98
  } else {
87
99
  status.centerCoordinate = mapView.centerCoordinate
@@ -120,6 +132,13 @@ class CameraManager {
120
132
  guard let mapView = mapView,
121
133
  let latitude = center["latitude"],
122
134
  let longitude = center["longitude"] else { return }
135
+
136
+ // 🔑 坐标验证
137
+ guard latitude >= -90 && latitude <= 90,
138
+ longitude >= -180 && longitude <= 180 else {
139
+ return
140
+ }
141
+
123
142
  mapView.setCenter(CLLocationCoordinate2D(latitude: latitude, longitude: longitude), animated: animated)
124
143
  }
125
144
 
@@ -129,7 +148,9 @@ class CameraManager {
129
148
  * @param animated 是否使用动画
130
149
  */
131
150
  func setZoomLevel(zoom: CGFloat, animated: Bool) {
132
- mapView?.setZoomLevel(zoom, animated: animated)
151
+ // 🔑 缩放级别范围限制 (3-20)
152
+ let validZoom = max(3, min(20, zoom))
153
+ mapView?.setZoomLevel(validZoom, animated: animated)
133
154
  }
134
155
 
135
156
  // MARK: - 相机信息获取
@@ -126,11 +126,16 @@ class UIManager: NSObject, MAMapViewDelegate {
126
126
  */
127
127
  public func mapView(_ mapView: MAMapView, didUpdate userLocation: MAUserLocation, updatingLocation: Bool) {
128
128
  guard updatingLocation, let location = userLocation.location else { return }
129
- onLocationChanged?(
130
- location.coordinate.latitude,
131
- location.coordinate.longitude,
132
- location.horizontalAccuracy
133
- )
129
+
130
+ // 🔑 坐标验证
131
+ let latitude = location.coordinate.latitude
132
+ let longitude = location.coordinate.longitude
133
+ guard latitude >= -90 && latitude <= 90,
134
+ longitude >= -180 && longitude <= 180 else {
135
+ return
136
+ }
137
+
138
+ onLocationChanged?(latitude, longitude, location.horizontalAccuracy)
134
139
  }
135
140
 
136
141
  /**
@@ -165,7 +165,11 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
165
165
  */
166
166
  func destroy() {
167
167
  locationManager?.stopUpdatingLocation()
168
+ locationManager?.stopUpdatingHeading()
169
+ locationManager?.delegate = nil
168
170
  locationManager = nil
171
+ onLocationUpdate = nil
172
+ onHeadingUpdate = nil
169
173
  }
170
174
 
171
175
  /**
@@ -194,6 +198,12 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
194
198
  * @param reGeocode 逆地理信息
195
199
  */
196
200
  func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!) {
201
+ // 🔑 坐标验证:防止无效坐标
202
+ guard location.coordinate.latitude >= -90 && location.coordinate.latitude <= 90,
203
+ location.coordinate.longitude >= -180 && location.coordinate.longitude <= 180 else {
204
+ return
205
+ }
206
+
197
207
  var locationData: [String: Any] = [
198
208
  "latitude": location.coordinate.latitude,
199
209
  "longitude": location.coordinate.longitude,
@@ -10,8 +10,8 @@ import MAMapKit
10
10
  * - 响应属性变化并更新渲染
11
11
  */
12
12
  class CircleView: ExpoView {
13
- /// 事件派发器
14
- let onPress = EventDispatcher()
13
+ /// 事件派发器 - 使用 onCirclePress 避免与 MarkerPress 冲突
14
+ let onCirclePress = EventDispatcher()
15
15
 
16
16
  /// 圆心坐标
17
17
  var circleCenter: [String: Double] = [:]
@@ -23,16 +23,47 @@ class CircleView: ExpoView {
23
23
  var strokeColor: Any?
24
24
  /// 边框宽度
25
25
  var strokeWidth: Float = 0
26
+ /// z-index 图层顺序
27
+ var zIndex: Double = 0
26
28
 
27
- /// 地图视图弱引用
29
+ /// 地图视图引用
28
30
  private var mapView: MAMapView?
29
31
  /// 圆形覆盖物对象
30
32
  var circle: MACircle?
31
33
  /// 圆形渲染器
32
34
  private var renderer: MACircleRenderer?
35
+ /// 上次设置的地图引用(防止重复调用)
36
+ private weak var lastSetMapView: MAMapView?
33
37
 
34
38
  required init(appContext: AppContext? = nil) {
35
39
  super.init(appContext: appContext)
40
+
41
+ // 🔑 关键修复:CircleView 不应该拦截触摸事件
42
+ self.isUserInteractionEnabled = false
43
+ }
44
+
45
+ /**
46
+ * 重写 hitTest,让触摸事件完全穿透此视图
47
+ * 这是解决覆盖物视图阻挡地图触摸的关键
48
+ */
49
+ override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
50
+ // 始终返回 nil,让触摸事件穿透到地图
51
+ return nil
52
+ }
53
+
54
+ /**
55
+ * 重写 point(inside:with:),确保此视图不响应任何触摸
56
+ */
57
+ override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
58
+ // 始终返回 false,表示点击不在此视图内
59
+ return false
60
+ }
61
+
62
+ /**
63
+ * 检查地图是否已连接
64
+ */
65
+ func isMapConnected() -> Bool {
66
+ return mapView != nil
36
67
  }
37
68
 
38
69
  /**
@@ -40,6 +71,12 @@ class CircleView: ExpoView {
40
71
  * @param map 地图视图
41
72
  */
42
73
  func setMap(_ map: MAMapView) {
74
+ // 🔑 关键优化:如果是同一个地图引用,跳过重复设置
75
+ if lastSetMapView === map {
76
+ return
77
+ }
78
+
79
+ lastSetMapView = map
43
80
  self.mapView = map
44
81
  updateCircle()
45
82
  }
@@ -48,32 +85,40 @@ class CircleView: ExpoView {
48
85
  * 更新圆形覆盖物
49
86
  */
50
87
  private func updateCircle() {
51
- guard let mapView = mapView,
52
- let latitude = circleCenter["latitude"],
88
+ guard let mapView = mapView else {
89
+ return
90
+ }
91
+
92
+ guard let latitude = circleCenter["latitude"],
53
93
  let longitude = circleCenter["longitude"],
54
94
  radius > 0 else {
55
- print("❌ CircleView.updateCircle: 条件不满足")
56
95
  return
57
96
  }
58
97
 
59
- print("🔵 CircleView.updateCircle: center=(\(latitude),\(longitude)), radius=\(radius)")
60
- print("🔵 CircleView.updateCircle: fillColor=\(String(describing: fillColor)), strokeColor=\(String(describing: strokeColor)), strokeWidth=\(strokeWidth)")
98
+ // 🔑 坐标验证:防止无效坐标导致崩溃
99
+ guard latitude >= -90 && latitude <= 90,
100
+ longitude >= -180 && longitude <= 180 else {
101
+ return
102
+ }
103
+
104
+ // 🔑 半径验证:防止负数或过大的半径
105
+ let validRadius = max(0.1, min(radius, 1000000))
61
106
 
62
107
  if circle == nil {
63
108
  let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
64
- circle = MACircle(center: coordinate, radius: radius)
109
+ circle = MACircle(center: coordinate, radius: validRadius)
65
110
  mapView.add(circle!)
66
- print("🔵 CircleView.updateCircle: 创建新圆形")
67
111
  } else {
68
- circle?.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
69
- circle?.radius = radius
112
+ // 先移除旧的
70
113
  mapView.remove(circle!)
114
+ // 更新属性
115
+ circle?.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
116
+ circle?.radius = validRadius
117
+ // 重新添加
71
118
  mapView.add(circle!)
72
- print("🔵 CircleView.updateCircle: 更新现有圆形")
73
119
  }
74
120
 
75
121
  renderer = nil
76
- print("🔵 CircleView.updateCircle: renderer 已清空")
77
122
  }
78
123
 
79
124
  /**
@@ -88,10 +133,10 @@ class CircleView: ExpoView {
88
133
  renderer?.fillColor = parsedFillColor ?? UIColor.clear
89
134
  renderer?.strokeColor = parsedStrokeColor ?? UIColor.clear
90
135
  renderer?.lineWidth = CGFloat(strokeWidth)
91
- print("🔵 CircleView.getRenderer: 创建新 renderer")
92
- print("🔵 CircleView.getRenderer: fillColor=\(String(describing: parsedFillColor)), strokeColor=\(String(describing: parsedStrokeColor)), lineWidth=\(strokeWidth)")
93
- } else {
94
- print("🔵 CircleView.getRenderer: 使用缓存的 renderer")
136
+ }
137
+ // 确保即使 renderer 存在,它也与当前的 circle 实例关联
138
+ if renderer?.circle !== circle {
139
+ renderer = MACircleRenderer(circle: circle)
95
140
  }
96
141
  return renderer!
97
142
  }
@@ -145,11 +190,45 @@ class CircleView: ExpoView {
145
190
  }
146
191
 
147
192
  /**
148
- * 析构时移除圆形
193
+ * 设置 z-index
194
+ * @param zIndex z-index 值,数值越大越在上层
195
+ *
196
+ * 注意:iOS 高德地图的 MACircle 不直接支持 zIndex 属性
197
+ * overlay 的渲染顺序由添加顺序决定,后添加的在上层
198
+ * 这里通过重新添加来尝试改变顺序
199
+ */
200
+ func setZIndex(_ zIndex: Double) {
201
+ self.zIndex = zIndex
202
+ renderer = nil
203
+ updateCircle()
204
+ }
205
+
206
+ /**
207
+ * 视图即将从父视图移除时调用
208
+ * 🔑 关键修复:旧架构下,React Native 移除视图时不一定立即调用 deinit
209
+ * 需要在 willMove(toSuperview:) 中立即清理地图覆盖物
210
+ */
211
+ override func willMove(toSuperview newSuperview: UIView?) {
212
+ super.willMove(toSuperview: newSuperview)
213
+
214
+ // 当 newSuperview 为 nil 时,表示视图正在从父视图移除
215
+ if newSuperview == nil {
216
+ if let mapView = mapView, let circle = circle {
217
+ mapView.remove(circle)
218
+ self.circle = nil
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * 析构时移除圆形(双重保险)
149
225
  */
150
226
  deinit {
151
227
  if let mapView = mapView, let circle = circle {
152
228
  mapView.remove(circle)
153
229
  }
230
+ mapView = nil
231
+ circle = nil
232
+ renderer = nil
154
233
  }
155
234
  }
@@ -5,6 +5,8 @@ public class CircleViewModule: Module {
5
5
  Name("CircleView")
6
6
 
7
7
  View(CircleView.self) {
8
+ Events("onCirclePress")
9
+
8
10
  Prop("center") { (view: CircleView, center: [String: Double]) in
9
11
  view.setCenter(center)
10
12
  }
@@ -24,6 +26,25 @@ public class CircleViewModule: Module {
24
26
  Prop("strokeWidth") { (view: CircleView, width: Double) in
25
27
  view.setStrokeWidth(Float(width))
26
28
  }
29
+
30
+ Prop("zIndex") { (view: CircleView, zIndex: Double) in
31
+ view.setZIndex(zIndex)
32
+ }
33
+
34
+ OnViewDidUpdateProps { (view: CircleView) in
35
+ // 属性更新完成后,如果还没连接地图,尝试连接
36
+ if !view.isMapConnected() {
37
+ // 查找父视图 ExpoGaodeMapView
38
+ var parent = view.superview
39
+ while parent != nil {
40
+ if let mapView = parent as? ExpoGaodeMapView {
41
+ view.setMap(mapView.mapView)
42
+ return
43
+ }
44
+ parent = parent?.superview
45
+ }
46
+ }
47
+ }
27
48
  }
28
49
  }
29
50
  }
@@ -36,10 +36,11 @@ class ClusterView: ExpoView {
36
36
  private func updateCluster() {
37
37
  guard let mapView = mapView else { return }
38
38
 
39
- for annotation in annotations {
40
- mapView.removeAnnotation(annotation)
41
- }
42
- annotations.removeAll()
39
+ // 先移除旧的注释
40
+ removeAllAnnotations()
41
+
42
+ // 验证数据有效性
43
+ guard !points.isEmpty else { return }
43
44
 
44
45
  for point in points {
45
46
  guard let latitude = point["latitude"] as? Double,
@@ -53,4 +54,32 @@ class ClusterView: ExpoView {
53
54
  annotations.append(annotation)
54
55
  }
55
56
  }
57
+
58
+ /**
59
+ * 移除所有标注
60
+ */
61
+ private func removeAllAnnotations() {
62
+ guard let mapView = mapView else { return }
63
+
64
+ for annotation in annotations {
65
+ mapView.removeAnnotation(annotation)
66
+ }
67
+ annotations.removeAll()
68
+ }
69
+
70
+ /**
71
+ * 从父视图移除时清理标注
72
+ */
73
+ override func removeFromSuperview() {
74
+ super.removeFromSuperview()
75
+ removeAllAnnotations()
76
+ }
77
+
78
+ /**
79
+ * 析构时移除标注
80
+ */
81
+ deinit {
82
+ removeAllAnnotations()
83
+ mapView = nil
84
+ }
56
85
  }
@@ -71,19 +71,30 @@ class HeatMapView: ExpoView {
71
71
  // 移除旧的热力图
72
72
  if let oldHeatmap = heatmapOverlay {
73
73
  mapView.remove(oldHeatmap)
74
+ heatmapOverlay = nil
74
75
  }
75
76
 
77
+ // 验证数据有效性
78
+ guard !data.isEmpty else { return }
79
+
76
80
  // 创建热力图数据
77
81
  var heatmapData: [MAHeatMapNode] = []
78
82
  for point in data {
79
83
  guard let latitude = point["latitude"] as? Double,
80
- let longitude = point["longitude"] as? Double else {
84
+ let longitude = point["longitude"] as? Double,
85
+ latitude >= -90 && latitude <= 90,
86
+ longitude >= -180 && longitude <= 180 else {
81
87
  continue
82
88
  }
83
89
 
84
90
  let node = MAHeatMapNode()
85
91
  node.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
86
- node.intensity = 1.0 // 默认强度
92
+ // 支持自定义强度,默认为 1.0
93
+ if let intensity = point["intensity"] as? Double {
94
+ node.intensity = Float(max(0, min(1, intensity)))
95
+ } else {
96
+ node.intensity = 1.0
97
+ }
87
98
  heatmapData.append(node)
88
99
  }
89
100
 
@@ -92,8 +103,8 @@ class HeatMapView: ExpoView {
92
103
  // 创建热力图图层
93
104
  let heatmap = MAHeatMapTileOverlay()
94
105
  heatmap.data = heatmapData
95
- heatmap.radius = radius
96
- heatmap.opacity = CGFloat(opacity)
106
+ heatmap.radius = max(1, radius) // 确保半径至少为 1
107
+ heatmap.opacity = CGFloat(max(0, min(1, opacity))) // 限制透明度范围
97
108
 
98
109
  mapView.add(heatmap)
99
110
  heatmapOverlay = heatmap
@@ -121,5 +132,6 @@ class HeatMapView: ExpoView {
121
132
  */
122
133
  deinit {
123
134
  removeHeatMap()
135
+ mapView = nil
124
136
  }
125
137
  }