expo-gaode-map-navigation 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +71 -45
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +35 -0
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +0 -1
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +13 -2
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +385 -1
- package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +180 -4
- package/android/src/main/java/expo/modules/gaodemap/navigation/services/IndependentRouteService.kt +182 -7
- package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +31 -1
- package/build/index.d.ts +7 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -0
- package/build/index.js.map +1 -1
- package/build/types/independent.types.d.ts +7 -0
- package/build/types/independent.types.d.ts.map +1 -1
- package/build/types/independent.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +5 -1
- package/build/types/native-module.types.d.ts.map +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/types/route.types.d.ts +112 -0
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js.map +1 -1
- package/ios/ExpoGaodeMapNaviView.swift +56 -4
- package/ios/ExpoGaodeMapNavigationModule.swift +585 -12
- package/ios/map/ExpoGaodeMapOfflineModule.swift +58 -34
- package/ios/map/GaodeMapPrivacyManager.swift +23 -1
- package/ios/map/overlays/MarkerView.swift +148 -11
- package/ios/services/IndependentRouteService.swift +186 -44
- package/package.json +1 -1
- package/plugin/build/withGaodeMap.js +28 -0
|
@@ -27,9 +27,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
27
27
|
set { stateQueue.async(flags: .barrier) { self._pausedCities = newValue } }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
private var offlineMapManager: MAOfflineMap
|
|
31
|
-
return MAOfflineMap.shared()
|
|
32
|
-
}
|
|
30
|
+
private var offlineMapManager: MAOfflineMap?
|
|
33
31
|
|
|
34
32
|
// 数据缓存
|
|
35
33
|
private var cachedCities: [MAOfflineCity]?
|
|
@@ -60,7 +58,11 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
OnDestroy {
|
|
63
|
-
|
|
61
|
+
self.getOfflineMapManager()?.cancelAll()
|
|
62
|
+
self.offlineMapManager = nil
|
|
63
|
+
self.isSetupComplete = false
|
|
64
|
+
self.isSetupInProgress = false
|
|
65
|
+
self.setupWaiters.removeAll()
|
|
64
66
|
self.downloadingCities.removeAll()
|
|
65
67
|
self.pausedCities.removeAll()
|
|
66
68
|
}
|
|
@@ -70,7 +72,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
70
72
|
AsyncFunction("getAvailableCities") { (promise: Promise) in
|
|
71
73
|
self.ensureSetup { success in
|
|
72
74
|
guard success else { promise.reject("ERR_SETUP", "Setup failed"); return }
|
|
73
|
-
let cities = self.cachedCities ?? self.
|
|
75
|
+
let cities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
74
76
|
promise.resolve(cities.map { self.convertCityToDict($0) })
|
|
75
77
|
}
|
|
76
78
|
}
|
|
@@ -78,7 +80,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
78
80
|
AsyncFunction("getAvailableProvinces") { (promise: Promise) in
|
|
79
81
|
self.ensureSetup { success in
|
|
80
82
|
guard success else { promise.reject("ERR_SETUP", "Setup failed"); return }
|
|
81
|
-
let provinces = self.cachedProvinces ?? self.
|
|
83
|
+
let provinces = self.cachedProvinces ?? self.getOfflineMapManager()?.provinces ?? []
|
|
82
84
|
promise.resolve(provinces.map { self.convertProvinceToDict($0) })
|
|
83
85
|
}
|
|
84
86
|
}
|
|
@@ -86,7 +88,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
86
88
|
AsyncFunction("getCitiesByProvince") { (provinceCode: String, promise: Promise) in
|
|
87
89
|
self.ensureSetup { success in
|
|
88
90
|
guard success else { promise.reject("ERR_SETUP", "Setup failed"); return }
|
|
89
|
-
let provinces = self.cachedProvinces ?? self.
|
|
91
|
+
let provinces = self.cachedProvinces ?? self.getOfflineMapManager()?.provinces ?? []
|
|
90
92
|
if let province = provinces.first(where: { $0.adcode == provinceCode }) {
|
|
91
93
|
let cities = province.cities.compactMap { ($0 as? MAOfflineCity).map { self.convertCityToDict($0) } }
|
|
92
94
|
promise.resolve(cities)
|
|
@@ -99,7 +101,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
99
101
|
AsyncFunction("getDownloadedMaps") { (promise: Promise) in
|
|
100
102
|
self.ensureSetup { success in
|
|
101
103
|
guard success else { promise.reject("ERR_SETUP", "Setup failed"); return }
|
|
102
|
-
let allCities = self.cachedCities ?? self.
|
|
104
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
103
105
|
let downloaded = allCities.filter { $0.itemStatus == .installed }
|
|
104
106
|
promise.resolve(downloaded.map { self.convertCityToDict($0) })
|
|
105
107
|
}
|
|
@@ -120,28 +122,28 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
Function("pauseDownload") { (cityCode: String) in
|
|
123
|
-
let allCities = self.cachedCities ?? self.
|
|
125
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
124
126
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
125
127
|
self.pausedCities.insert(cityCode)
|
|
126
128
|
self.downloadingCities.remove(cityCode)
|
|
127
|
-
self.
|
|
129
|
+
self.getOfflineMapManager()?.pause(city)
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
AsyncFunction("cancelDownload") { (cityCode: String) in
|
|
132
|
-
let allCities = self.cachedCities ?? self.
|
|
134
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
133
135
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
134
136
|
self.downloadingCities.remove(cityCode)
|
|
135
137
|
self.pausedCities.remove(cityCode)
|
|
136
|
-
self.
|
|
138
|
+
self.getOfflineMapManager()?.pause(city) // iOS SDK 中 pause 停止网络
|
|
137
139
|
self.sendEvent("onDownloadCancelled", ["cityCode": cityCode, "cityName": city.name ?? ""])
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
AsyncFunction("deleteMap") { (cityCode: String) in
|
|
142
|
-
let allCities = self.cachedCities ?? self.
|
|
144
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
143
145
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
144
|
-
self.
|
|
146
|
+
self.getOfflineMapManager()?.delete(city)
|
|
145
147
|
self.downloadingCities.remove(cityCode)
|
|
146
148
|
self.pausedCities.remove(cityCode)
|
|
147
149
|
}
|
|
@@ -153,7 +155,11 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
153
155
|
|
|
154
156
|
AsyncFunction("checkUpdate") { (cityCode: String, promise: Promise) in
|
|
155
157
|
// 检查特定城市或全局更新
|
|
156
|
-
self.
|
|
158
|
+
guard let manager = self.getOfflineMapManager() else {
|
|
159
|
+
promise.reject("ERR_PRIVACY", "Privacy consent is required before using offline map APIs")
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
manager.checkNewestVersion { hasNewestVersion in
|
|
157
163
|
if hasNewestVersion {
|
|
158
164
|
// 刷新缓存以获取最新数据
|
|
159
165
|
self.stateQueue.async(flags: .barrier) {
|
|
@@ -169,13 +175,13 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
169
175
|
// ==================== 3. 状态查询 ====================
|
|
170
176
|
|
|
171
177
|
AsyncFunction("isMapDownloaded") { (cityCode: String) -> Bool in
|
|
172
|
-
let allCities = self.cachedCities ?? self.
|
|
178
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
173
179
|
return allCities.first(where: { $0.adcode == cityCode })?.itemStatus == .installed
|
|
174
180
|
}
|
|
175
181
|
|
|
176
182
|
// 恢复原有方法:getMapStatus
|
|
177
183
|
AsyncFunction("getMapStatus") { (cityCode: String) -> [String: Any] in
|
|
178
|
-
let allCities = self.cachedCities ?? self.
|
|
184
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
179
185
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
180
186
|
return self.convertCityToDict(city)
|
|
181
187
|
}
|
|
@@ -194,7 +200,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
194
200
|
// ==================== 4. 存储管理 ====================
|
|
195
201
|
|
|
196
202
|
AsyncFunction("getStorageSize") { () -> Int64 in
|
|
197
|
-
let allCities = self.cachedCities ?? self.
|
|
203
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
198
204
|
let installed = allCities.filter { $0.itemStatus == .installed }
|
|
199
205
|
// 修复类型转换报错:Int64(0)
|
|
200
206
|
return installed.reduce(Int64(0)) { $0 + ($1.downloadedSize > 0 ? $1.downloadedSize : $1.size) }
|
|
@@ -203,7 +209,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
203
209
|
// 恢复原有方法:getStorageInfo
|
|
204
210
|
AsyncFunction("getStorageInfo") { () -> [String: Any] in
|
|
205
211
|
// 1. 计算离线地图占用
|
|
206
|
-
let allCities = self.cachedCities ?? self.
|
|
212
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
207
213
|
let installed = allCities.filter { $0.itemStatus == .installed }
|
|
208
214
|
let offlineMapSize = installed.reduce(Int64(0)) { $0 + ($1.downloadedSize > 0 ? $1.downloadedSize : $1.size) }
|
|
209
215
|
|
|
@@ -231,7 +237,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
231
237
|
}
|
|
232
238
|
|
|
233
239
|
AsyncFunction("clearAllMaps") {
|
|
234
|
-
self.
|
|
240
|
+
self.getOfflineMapManager()?.clearDisk()
|
|
235
241
|
self.downloadingCities.removeAll()
|
|
236
242
|
self.pausedCities.removeAll()
|
|
237
243
|
self.cachedCities = nil
|
|
@@ -254,14 +260,14 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
254
260
|
// 确保初始化
|
|
255
261
|
self.ensureSetup { success in
|
|
256
262
|
guard success else { return }
|
|
257
|
-
let allCities = self.cachedCities ?? self.
|
|
263
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
258
264
|
|
|
259
265
|
cityCodes.forEach { cityCode in
|
|
260
266
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
261
267
|
self.downloadingCities.insert(cityCode)
|
|
262
268
|
self.pausedCities.remove(cityCode)
|
|
263
269
|
// 批量下载也建议开启后台
|
|
264
|
-
self.
|
|
270
|
+
self.getOfflineMapManager()?.downloadItem(city, shouldContinueWhenAppEntersBackground: true) { [weak self] item, status, info in
|
|
265
271
|
guard let self = self, let item = item else { return }
|
|
266
272
|
self.handleDownloadCallback(item: item, status: status, info: info)
|
|
267
273
|
}
|
|
@@ -272,10 +278,10 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
272
278
|
|
|
273
279
|
// 恢复原有方法:batchDelete
|
|
274
280
|
AsyncFunction("batchDelete") { (cityCodes: [String]) in
|
|
275
|
-
let allCities = self.cachedCities ?? self.
|
|
281
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
276
282
|
cityCodes.forEach { cityCode in
|
|
277
283
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
278
|
-
self.
|
|
284
|
+
self.getOfflineMapManager()?.delete(city)
|
|
279
285
|
}
|
|
280
286
|
self.downloadingCities.remove(cityCode)
|
|
281
287
|
self.pausedCities.remove(cityCode)
|
|
@@ -286,7 +292,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
286
292
|
AsyncFunction("batchUpdate") { (cityCodes: [String]) in
|
|
287
293
|
self.ensureSetup { success in
|
|
288
294
|
guard success else { return }
|
|
289
|
-
let allCities = self.cachedCities ?? self.
|
|
295
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
290
296
|
|
|
291
297
|
cityCodes.forEach { cityCode in
|
|
292
298
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
@@ -296,7 +302,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
296
302
|
self._downloadingCities.insert(cityCode)
|
|
297
303
|
self._pausedCities.remove(cityCode)
|
|
298
304
|
}
|
|
299
|
-
self.
|
|
305
|
+
self.getOfflineMapManager()?.downloadItem(city, shouldContinueWhenAppEntersBackground: true) { [weak self] item, status, info in
|
|
300
306
|
guard let self = self, let item = item else { return }
|
|
301
307
|
self.handleDownloadCallback(item: item, status: status, info: info)
|
|
302
308
|
}
|
|
@@ -307,7 +313,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
307
313
|
}
|
|
308
314
|
|
|
309
315
|
AsyncFunction("pauseAllDownloads") {
|
|
310
|
-
self.
|
|
316
|
+
self.getOfflineMapManager()?.cancelAll()
|
|
311
317
|
for cityCode in self.downloadingCities {
|
|
312
318
|
self.pausedCities.insert(cityCode)
|
|
313
319
|
self.sendEvent("onDownloadPaused", ["cityCode": cityCode, "cityName": ""])
|
|
@@ -320,14 +326,14 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
320
326
|
// iOS SDK 没有 resumeAll,只能尝试恢复 pausedCities 列表中的城市
|
|
321
327
|
self.ensureSetup { success in
|
|
322
328
|
guard success else { return }
|
|
323
|
-
let allCities = self.cachedCities ?? self.
|
|
329
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
324
330
|
let pausedList = Array(self.pausedCities)
|
|
325
331
|
|
|
326
332
|
pausedList.forEach { cityCode in
|
|
327
333
|
if let city = allCities.first(where: { $0.adcode == cityCode }) {
|
|
328
334
|
self.pausedCities.remove(cityCode)
|
|
329
335
|
self.downloadingCities.insert(cityCode)
|
|
330
|
-
self.
|
|
336
|
+
self.getOfflineMapManager()?.downloadItem(city, shouldContinueWhenAppEntersBackground: true) { [weak self] item, status, info in
|
|
331
337
|
guard let self = self, let item = item else { return }
|
|
332
338
|
self.handleDownloadCallback(item: item, status: status, info: info)
|
|
333
339
|
}
|
|
@@ -346,7 +352,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
346
352
|
return
|
|
347
353
|
}
|
|
348
354
|
|
|
349
|
-
let allCities = self.cachedCities ?? self.
|
|
355
|
+
let allCities = self.cachedCities ?? self.getOfflineMapManager()?.cities ?? []
|
|
350
356
|
guard let city = allCities.first(where: { $0.adcode == cityCode }) else {
|
|
351
357
|
promise.reject("ERR_CITY", "City not found: \(cityCode)")
|
|
352
358
|
return
|
|
@@ -356,21 +362,39 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
356
362
|
self.pausedCities.remove(cityCode)
|
|
357
363
|
|
|
358
364
|
// 开启后台下载
|
|
359
|
-
self.
|
|
365
|
+
self.getOfflineMapManager()?.downloadItem(city, shouldContinueWhenAppEntersBackground: true) { [weak self] item, status, info in
|
|
360
366
|
guard let self = self, let item = item else { return }
|
|
361
367
|
self.handleDownloadCallback(item: item, status: status, info: info)
|
|
362
368
|
}
|
|
363
369
|
promise.resolve(true)
|
|
364
370
|
}
|
|
365
371
|
}
|
|
372
|
+
|
|
373
|
+
private func getOfflineMapManager() -> MAOfflineMap? {
|
|
374
|
+
GaodeMapPrivacyManager.restorePersistedState()
|
|
375
|
+
guard GaodeMapPrivacyManager.isReady else {
|
|
376
|
+
return nil
|
|
377
|
+
}
|
|
378
|
+
GaodeMapPrivacyManager.applyPrivacyState()
|
|
379
|
+
|
|
380
|
+
if offlineMapManager == nil {
|
|
381
|
+
offlineMapManager = MAOfflineMap.shared()
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return offlineMapManager
|
|
385
|
+
}
|
|
366
386
|
|
|
367
387
|
private func ensureSetup(completion: @escaping (Bool) -> Void) {
|
|
388
|
+
guard let manager = getOfflineMapManager() else {
|
|
389
|
+
completion(false)
|
|
390
|
+
return
|
|
391
|
+
}
|
|
368
392
|
if isSetupComplete { completion(true); return }
|
|
369
393
|
setupWaiters.append(completion)
|
|
370
394
|
if isSetupInProgress { return }
|
|
371
395
|
|
|
372
396
|
isSetupInProgress = true
|
|
373
|
-
|
|
397
|
+
manager.setup { [weak self] success in
|
|
374
398
|
guard let self = self else { return }
|
|
375
399
|
self.isSetupInProgress = false
|
|
376
400
|
self.isSetupComplete = success
|
|
@@ -382,7 +406,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
382
406
|
}
|
|
383
407
|
|
|
384
408
|
private func parseOfflineMapData() {
|
|
385
|
-
let map =
|
|
409
|
+
let map = getOfflineMapManager()
|
|
386
410
|
stateQueue.async(flags: .barrier) {
|
|
387
411
|
self.cachedCities = map?.cities
|
|
388
412
|
self.cachedProvinces = map?.provinces
|
|
@@ -462,7 +486,7 @@ public class ExpoGaodeMapOfflineModule: Module {
|
|
|
462
486
|
"size": city.size,
|
|
463
487
|
"status": status,
|
|
464
488
|
"downloadedSize": city.downloadedSize,
|
|
465
|
-
"version": self.
|
|
489
|
+
"version": self.getOfflineMapManager()?.version ?? "",
|
|
466
490
|
"progress": city.size > 0 ? Int((Double(city.downloadedSize) / Double(city.size)) * 100) : 0
|
|
467
491
|
]
|
|
468
492
|
}
|
|
@@ -89,18 +89,40 @@ enum GaodeMapPrivacyManager {
|
|
|
89
89
|
applyPrivacyState()
|
|
90
90
|
notifyIfNeeded(previousStatus: previousStatus)
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
static func applyPrivacyState() {
|
|
94
94
|
let showStatus: AMapPrivacyShowStatus = hasShow ? .didShow : .notShow
|
|
95
95
|
let infoStatus: AMapPrivacyInfoStatus = hasContainsPrivacy ? .didContain : .notContain
|
|
96
96
|
let agreeStatus: AMapPrivacyAgreeStatus = hasAgree ? .didAgree : .notAgree
|
|
97
97
|
|
|
98
|
+
// Map & Location(类方法 ✅)
|
|
98
99
|
MAMapView.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
99
100
|
MAMapView.updatePrivacyAgree(agreeStatus)
|
|
100
101
|
AMapLocationManager.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
101
102
|
AMapLocationManager.updatePrivacyAgree(agreeStatus)
|
|
103
|
+
|
|
104
|
+
// Navi(实例方法 ⚠️)
|
|
105
|
+
let naviConfig = AMapNaviManagerConfig()
|
|
106
|
+
naviConfig.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
107
|
+
naviConfig.updatePrivacyAgree(agreeStatus)
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
// static func applyPrivacyState() {
|
|
111
|
+
// let showStatus: AMapPrivacyShowStatus = hasShow ? .didShow : .notShow
|
|
112
|
+
// let infoStatus: AMapPrivacyInfoStatus = hasContainsPrivacy ? .didContain : .notContain
|
|
113
|
+
// let agreeStatus: AMapPrivacyAgreeStatus = hasAgree ? .didAgree : .notAgree
|
|
114
|
+
//
|
|
115
|
+
// MAMapView.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
116
|
+
// MAMapView.updatePrivacyAgree(agreeStatus)
|
|
117
|
+
// AMapLocationManager.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
118
|
+
// AMapLocationManager.updatePrivacyAgree(agreeStatus)
|
|
119
|
+
// let config = AMapNaviManagerConfig()
|
|
120
|
+
// config.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
121
|
+
// config.updatePrivacyAgree(agreeStatus)
|
|
122
|
+
//// AMapNaviManagerConfig.updatePrivacyShow(showStatus, privacyInfo: infoStatus)
|
|
123
|
+
//// AMapNaviManagerConfig.updatePrivacyAgree(agreeStatus)
|
|
124
|
+
// }
|
|
125
|
+
|
|
104
126
|
static func status() -> [String: Any] {
|
|
105
127
|
[
|
|
106
128
|
"hasShow": hasShow,
|
|
@@ -154,9 +154,9 @@ class MarkerView: ExpoView {
|
|
|
154
154
|
|
|
155
155
|
// JS 侧可以调用
|
|
156
156
|
func setCacheKey(_ key: String?) {
|
|
157
|
+
guard cacheKey != key else { return }
|
|
157
158
|
self.cacheKey = key
|
|
158
|
-
|
|
159
|
-
updateAnnotation()
|
|
159
|
+
refreshAnnotationAppearance(invalidateChildrenCache: !subviews.isEmpty)
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/**
|
|
@@ -246,8 +246,6 @@ class MarkerView: ExpoView {
|
|
|
246
246
|
if let generated = self.createImageFromSubviews() {
|
|
247
247
|
annotationView.image = generated
|
|
248
248
|
self.applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
249
|
-
} else if self.hasPendingImageContent() {
|
|
250
|
-
self.scheduleSubviewRefresh(allowFallbackToDefault: false)
|
|
251
249
|
}
|
|
252
250
|
}
|
|
253
251
|
return annotationView
|
|
@@ -255,10 +253,12 @@ class MarkerView: ExpoView {
|
|
|
255
253
|
|
|
256
254
|
// 2. 如果有 icon 属性,使用自定义图标
|
|
257
255
|
if let iconUri = iconUri, !iconUri.isEmpty {
|
|
258
|
-
let key =
|
|
256
|
+
let key = iconCacheKey(for: iconUri)
|
|
259
257
|
if let cached = IconBitmapCache.shared.image(forKey: key) {
|
|
260
258
|
annotationView?.image = cached
|
|
261
|
-
annotationView
|
|
259
|
+
if let annotationView = annotationView {
|
|
260
|
+
applyCenterOffset(to: annotationView, defaultOffset: CGPoint(x: 0, y: -cached.size.height / 2))
|
|
261
|
+
}
|
|
262
262
|
return annotationView
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -274,7 +274,7 @@ class MarkerView: ExpoView {
|
|
|
274
274
|
if let img = resizedImage {
|
|
275
275
|
IconBitmapCache.shared.setImage(img, forKey: key)
|
|
276
276
|
annotationView.image = img
|
|
277
|
-
|
|
277
|
+
self.applyCenterOffset(to: annotationView, defaultOffset: CGPoint(x: 0, y: -img.size.height / 2))
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
return annotationView
|
|
@@ -391,10 +391,12 @@ class MarkerView: ExpoView {
|
|
|
391
391
|
self.annotationView = annotationView
|
|
392
392
|
|
|
393
393
|
// 构建 key
|
|
394
|
-
let key =
|
|
394
|
+
let key = iconCacheKey(for: iconUri)
|
|
395
395
|
if let cached = IconBitmapCache.shared.image(forKey: key) {
|
|
396
396
|
annotationView?.image = cached
|
|
397
|
-
annotationView
|
|
397
|
+
if let annotationView = annotationView {
|
|
398
|
+
applyCenterOffset(to: annotationView, defaultOffset: CGPoint(x: 0, y: -cached.size.height / 2))
|
|
399
|
+
}
|
|
398
400
|
return annotationView
|
|
399
401
|
}
|
|
400
402
|
|
|
@@ -411,7 +413,9 @@ class MarkerView: ExpoView {
|
|
|
411
413
|
if let img = resizedImage {
|
|
412
414
|
IconBitmapCache.shared.setImage(img, forKey: key)
|
|
413
415
|
annotationView?.image = img
|
|
414
|
-
annotationView
|
|
416
|
+
if let annotationView = annotationView {
|
|
417
|
+
self.applyCenterOffset(to: annotationView, defaultOffset: CGPoint(x: 0, y: -img.size.height / 2))
|
|
418
|
+
}
|
|
415
419
|
}
|
|
416
420
|
}
|
|
417
421
|
}
|
|
@@ -451,6 +455,9 @@ class MarkerView: ExpoView {
|
|
|
451
455
|
pinView?.canShowCallout = canShowCallout
|
|
452
456
|
pinView?.isDraggable = draggable
|
|
453
457
|
pinView?.animatesDrop = animatesDrop
|
|
458
|
+
if let pinView = pinView {
|
|
459
|
+
applyCenterOffset(to: pinView, defaultOffset: .zero)
|
|
460
|
+
}
|
|
454
461
|
|
|
455
462
|
self.annotationView = pinView
|
|
456
463
|
return pinView
|
|
@@ -538,6 +545,41 @@ class MarkerView: ExpoView {
|
|
|
538
545
|
return image
|
|
539
546
|
}
|
|
540
547
|
|
|
548
|
+
private func iconCacheKey(for iconUri: String) -> String {
|
|
549
|
+
let baseKey = cacheKey ?? "icon|\(iconUri)"
|
|
550
|
+
return "\(baseKey)|\(Int(iconWidth.rounded()))x\(Int(iconHeight.rounded()))"
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private func applyCenterOffset(to annotationView: MAAnnotationView, defaultOffset: CGPoint) {
|
|
554
|
+
annotationView.centerOffset = resolvedCenterOffset(defaultOffset: defaultOffset)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private func resolvedCenterOffset(defaultOffset: CGPoint) -> CGPoint {
|
|
558
|
+
guard let centerOffset else {
|
|
559
|
+
return defaultOffset
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
let x = centerOffset["x"] ?? Double(defaultOffset.x)
|
|
563
|
+
let y = centerOffset["y"] ?? Double(defaultOffset.y)
|
|
564
|
+
return CGPoint(x: x, y: y)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private func currentDefaultCenterOffset() -> CGPoint {
|
|
568
|
+
if !subviews.isEmpty {
|
|
569
|
+
return .zero
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if let image = annotationView?.image ?? animatedAnnotationView?.image {
|
|
573
|
+
return CGPoint(x: 0, y: -image.size.height / 2)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if let iconUri, !iconUri.isEmpty {
|
|
577
|
+
return CGPoint(x: 0, y: -iconHeight / 2)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return .zero
|
|
581
|
+
}
|
|
582
|
+
|
|
541
583
|
private func resolvedCustomSubviewSize(defaultSize: CGSize) -> CGSize {
|
|
542
584
|
guard let firstSubview = subviews.first else {
|
|
543
585
|
return defaultSize
|
|
@@ -808,6 +850,45 @@ class MarkerView: ExpoView {
|
|
|
808
850
|
IconBitmapCache.shared.removeImage(forKey: childrenCacheKey(for: size))
|
|
809
851
|
}
|
|
810
852
|
}
|
|
853
|
+
|
|
854
|
+
private func refreshAnnotationAppearance(invalidateChildrenCache: Bool = false) {
|
|
855
|
+
guard !isRemoving else { return }
|
|
856
|
+
|
|
857
|
+
let refresh = { [weak self] in
|
|
858
|
+
guard let self = self, let mapView = self.mapView else { return }
|
|
859
|
+
|
|
860
|
+
self.pendingSubviewRefreshTask?.cancel()
|
|
861
|
+
self.pendingSubviewRefreshTask = nil
|
|
862
|
+
|
|
863
|
+
if invalidateChildrenCache {
|
|
864
|
+
self.lastRenderedChildrenSignature = nil
|
|
865
|
+
if !self.subviews.isEmpty {
|
|
866
|
+
self.invalidateCurrentChildrenCache()
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if let animatedAnnotation = self.animatedAnnotation {
|
|
871
|
+
self.animatedAnnotationView = nil
|
|
872
|
+
mapView.removeAnnotation(animatedAnnotation)
|
|
873
|
+
mapView.addAnnotation(animatedAnnotation)
|
|
874
|
+
return
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if let annotation = self.annotation {
|
|
878
|
+
self.annotationView = nil
|
|
879
|
+
mapView.removeAnnotation(annotation)
|
|
880
|
+
mapView.addAnnotation(annotation)
|
|
881
|
+
} else {
|
|
882
|
+
self.updateAnnotation()
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if Thread.isMainThread {
|
|
887
|
+
refresh()
|
|
888
|
+
} else {
|
|
889
|
+
DispatchQueue.main.async(execute: refresh)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
811
892
|
|
|
812
893
|
/**
|
|
813
894
|
* 设置纬度
|
|
@@ -893,28 +974,83 @@ class MarkerView: ExpoView {
|
|
|
893
974
|
*/
|
|
894
975
|
func setDraggable(_ draggable: Bool) {
|
|
895
976
|
self.draggable = draggable
|
|
977
|
+
annotationView?.isDraggable = draggable
|
|
978
|
+
animatedAnnotationView?.isDraggable = draggable
|
|
896
979
|
updateAnnotation()
|
|
897
980
|
}
|
|
898
981
|
|
|
899
982
|
func setIconUri(_ uri: String?) {
|
|
900
983
|
self.iconUri = uri
|
|
901
|
-
|
|
984
|
+
refreshAnnotationAppearance()
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
func setIconWidth(_ width: Double) {
|
|
988
|
+
guard iconWidth != width else { return }
|
|
989
|
+
iconWidth = width
|
|
990
|
+
if let iconUri, !iconUri.isEmpty {
|
|
991
|
+
refreshAnnotationAppearance()
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
func setIconHeight(_ height: Double) {
|
|
996
|
+
guard iconHeight != height else { return }
|
|
997
|
+
iconHeight = height
|
|
998
|
+
if let iconUri, !iconUri.isEmpty {
|
|
999
|
+
refreshAnnotationAppearance()
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
func setCustomViewWidth(_ width: Double) {
|
|
1004
|
+
guard customViewWidth != width else { return }
|
|
1005
|
+
customViewWidth = width
|
|
1006
|
+
if !subviews.isEmpty {
|
|
1007
|
+
refreshAnnotationAppearance(invalidateChildrenCache: true)
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
func setCustomViewHeight(_ height: Double) {
|
|
1012
|
+
guard customViewHeight != height else { return }
|
|
1013
|
+
customViewHeight = height
|
|
1014
|
+
if !subviews.isEmpty {
|
|
1015
|
+
refreshAnnotationAppearance(invalidateChildrenCache: true)
|
|
1016
|
+
}
|
|
902
1017
|
}
|
|
903
1018
|
|
|
904
1019
|
func setCenterOffset(_ offset: [String: Double]) {
|
|
905
1020
|
self.centerOffset = offset
|
|
1021
|
+
if let annotationView = annotationView {
|
|
1022
|
+
applyCenterOffset(to: annotationView, defaultOffset: currentDefaultCenterOffset())
|
|
1023
|
+
}
|
|
1024
|
+
if let animatedAnnotationView = animatedAnnotationView {
|
|
1025
|
+
applyCenterOffset(to: animatedAnnotationView, defaultOffset: currentDefaultCenterOffset())
|
|
1026
|
+
}
|
|
906
1027
|
}
|
|
907
1028
|
|
|
908
1029
|
func setAnimatesDrop(_ animate: Bool) {
|
|
909
1030
|
self.animatesDrop = animate
|
|
1031
|
+
(annotationView as? MAPinAnnotationView)?.animatesDrop = animate
|
|
910
1032
|
}
|
|
911
1033
|
|
|
912
1034
|
func setPinColor(_ color: String) {
|
|
1035
|
+
guard pinColor != color else { return }
|
|
913
1036
|
self.pinColor = color
|
|
1037
|
+
if subviews.isEmpty && (iconUri?.isEmpty ?? true) {
|
|
1038
|
+
refreshAnnotationAppearance()
|
|
1039
|
+
}
|
|
914
1040
|
}
|
|
915
1041
|
|
|
916
1042
|
func setCanShowCallout(_ show: Bool) {
|
|
917
1043
|
self.canShowCallout = show
|
|
1044
|
+
if subviews.isEmpty {
|
|
1045
|
+
annotationView?.canShowCallout = show
|
|
1046
|
+
animatedAnnotationView?.canShowCallout = show
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
func setGrowAnimation(_ enabled: Bool) {
|
|
1051
|
+
guard growAnimation != enabled else { return }
|
|
1052
|
+
growAnimation = enabled
|
|
1053
|
+
refreshAnnotationAppearance()
|
|
918
1054
|
}
|
|
919
1055
|
|
|
920
1056
|
// MARK: - 平滑移动相关方法
|
|
@@ -1125,6 +1261,7 @@ class MarkerView: ExpoView {
|
|
|
1125
1261
|
// 取消待处理的任务
|
|
1126
1262
|
pendingAddTask?.cancel()
|
|
1127
1263
|
pendingUpdateTask?.cancel()
|
|
1264
|
+
pendingSubviewRefreshTask?.cancel()
|
|
1128
1265
|
|
|
1129
1266
|
// 清理引用,防止内存泄漏
|
|
1130
1267
|
mapView = nil
|