expo-gaode-map-navigation 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -1
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +182 -86
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +5 -2
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +19 -5
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +319 -48
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +3 -3
- package/build/index.d.ts +8 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +79 -1
- package/build/index.js.map +1 -1
- package/build/map/ExpoGaodeMapModule.d.ts +4 -4
- package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapModule.js +10 -8
- package/build/map/ExpoGaodeMapModule.js.map +1 -1
- package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapView.js +79 -17
- package/build/map/ExpoGaodeMapView.js.map +1 -1
- package/build/map/components/overlays/Cluster.d.ts.map +1 -1
- package/build/map/components/overlays/Cluster.js +12 -0
- package/build/map/components/overlays/Cluster.js.map +1 -1
- package/build/map/components/overlays/Marker.d.ts.map +1 -1
- package/build/map/components/overlays/Marker.js +70 -6
- package/build/map/components/overlays/Marker.js.map +1 -1
- package/build/map/types/common.types.d.ts +29 -5
- package/build/map/types/common.types.d.ts.map +1 -1
- package/build/map/types/common.types.js +5 -5
- package/build/map/types/common.types.js.map +1 -1
- package/build/map/types/index.d.ts +2 -1
- package/build/map/types/index.d.ts.map +1 -1
- package/build/map/types/index.js.map +1 -1
- package/build/map/types/location.types.d.ts +23 -0
- package/build/map/types/location.types.d.ts.map +1 -1
- package/build/map/types/location.types.js.map +1 -1
- package/build/map/types/map-view.types.d.ts +20 -22
- package/build/map/types/map-view.types.d.ts.map +1 -1
- package/build/map/types/map-view.types.js.map +1 -1
- package/build/map/types/overlays.types.d.ts +9 -2
- package/build/map/types/overlays.types.d.ts.map +1 -1
- package/build/map/types/overlays.types.js.map +1 -1
- package/build/map/types/route-playback.types.d.ts +12 -0
- package/build/map/types/route-playback.types.d.ts.map +1 -0
- package/build/map/types/route-playback.types.js +2 -0
- package/build/map/types/route-playback.types.js.map +1 -0
- package/build/types/route.types.d.ts +10 -1
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js +2 -0
- package/build/types/route.types.js.map +1 -1
- package/ios/map/ExpoGaodeMapView.swift +151 -76
- package/ios/map/ExpoGaodeMapViewModule.swift +14 -1
- package/ios/map/managers/UIManager.swift +5 -4
- package/ios/map/overlays/ClusterView.swift +207 -147
- package/ios/map/overlays/ClusterViewModule.swift +5 -1
- package/ios/map/overlays/MarkerView.swift +214 -60
- package/ios/map/overlays/MarkerViewModule.swift +1 -1
- package/package.json +1 -1
|
@@ -73,6 +73,10 @@ class MarkerView: ExpoView {
|
|
|
73
73
|
private var pendingAddTask: DispatchWorkItem?
|
|
74
74
|
/// 延迟更新任务(批量处理 props 更新)
|
|
75
75
|
private var pendingUpdateTask: DispatchWorkItem?
|
|
76
|
+
/// 子视图变化后的延迟刷新任务
|
|
77
|
+
private var pendingSubviewRefreshTask: DispatchWorkItem?
|
|
78
|
+
/// 最近一次应用到 annotationView 的 children 结构签名
|
|
79
|
+
private var lastRenderedChildrenSignature: String?
|
|
76
80
|
/// 上次设置的地图引用(防止重复调用)
|
|
77
81
|
private weak var lastSetMapView: MAMapView?
|
|
78
82
|
|
|
@@ -226,10 +230,13 @@ class MarkerView: ExpoView {
|
|
|
226
230
|
|
|
227
231
|
// 1. 如果有 children,使用自定义视图
|
|
228
232
|
if self.subviews.count > 0 {
|
|
229
|
-
let
|
|
233
|
+
let size = resolvedCustomSubviewSize(defaultSize: CGSize(width: 200, height: 60))
|
|
234
|
+
let key = childrenCacheKey(for: size)
|
|
230
235
|
if let cached = IconBitmapCache.shared.image(forKey: key) {
|
|
231
236
|
annotationView?.image = cached
|
|
232
|
-
annotationView
|
|
237
|
+
if let annotationView = annotationView {
|
|
238
|
+
applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
239
|
+
}
|
|
233
240
|
return annotationView
|
|
234
241
|
}
|
|
235
242
|
|
|
@@ -237,9 +244,10 @@ class MarkerView: ExpoView {
|
|
|
237
244
|
DispatchQueue.main.async { [weak self, weak annotationView] in
|
|
238
245
|
guard let self = self, let annotationView = annotationView else { return }
|
|
239
246
|
if let generated = self.createImageFromSubviews() {
|
|
240
|
-
IconBitmapCache.shared.setImage(generated, forKey: key)
|
|
241
247
|
annotationView.image = generated
|
|
242
|
-
|
|
248
|
+
self.applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
249
|
+
} else if self.hasPendingImageContent() {
|
|
250
|
+
self.scheduleSubviewRefresh(allowFallbackToDefault: false)
|
|
243
251
|
}
|
|
244
252
|
}
|
|
245
253
|
return annotationView
|
|
@@ -318,19 +326,19 @@ class MarkerView: ExpoView {
|
|
|
318
326
|
self.annotationView = annotationView
|
|
319
327
|
|
|
320
328
|
// 生成 cacheKey 或 fallback 到 identifier
|
|
321
|
-
let
|
|
329
|
+
let size = resolvedCustomSubviewSize(defaultSize: CGSize(width: 200, height: 40))
|
|
330
|
+
let key = childrenCacheKey(for: size)
|
|
322
331
|
|
|
323
332
|
// 1) 如果缓存命中,直接同步返回图像(fast path)
|
|
324
333
|
if let cached = IconBitmapCache.shared.image(forKey: key) {
|
|
325
334
|
annotationView?.image = cached
|
|
326
|
-
|
|
327
|
-
|
|
335
|
+
if let annotationView = annotationView {
|
|
336
|
+
applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
337
|
+
}
|
|
328
338
|
return annotationView
|
|
329
339
|
}
|
|
330
340
|
|
|
331
341
|
// 2) 缓存未命中:返回占位(透明),并异步在主线程生成图像然后回填
|
|
332
|
-
let size = CGSize(width: CGFloat(customViewWidth > 0 ? customViewWidth : 200),
|
|
333
|
-
height: CGFloat(customViewHeight > 0 ? customViewHeight : 40))
|
|
334
342
|
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
|
335
343
|
let transparentImage = UIGraphicsGetImageFromCurrentImageContext()
|
|
336
344
|
UIGraphicsEndImageContext()
|
|
@@ -342,17 +350,16 @@ class MarkerView: ExpoView {
|
|
|
342
350
|
// 再次检查缓存(避免重复渲染)
|
|
343
351
|
if let cached = IconBitmapCache.shared.image(forKey: key) {
|
|
344
352
|
annotationView.image = cached
|
|
345
|
-
|
|
353
|
+
self.applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
346
354
|
return
|
|
347
355
|
}
|
|
348
356
|
|
|
349
357
|
// 调用你的原生渲染逻辑(保留空白检测、多次 layout)
|
|
350
358
|
if let generated = self.createImageFromSubviews() {
|
|
351
|
-
// 写入缓存(仅当用户传了 cacheKey 才缓存;否则建议仍缓存由 fingerprint 决定)
|
|
352
|
-
IconBitmapCache.shared.setImage(generated, forKey: key)
|
|
353
359
|
annotationView.image = generated
|
|
354
|
-
|
|
355
|
-
} else {
|
|
360
|
+
self.applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
361
|
+
} else if self.hasPendingImageContent() {
|
|
362
|
+
self.scheduleSubviewRefresh(allowFallbackToDefault: false)
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -482,37 +489,21 @@ class MarkerView: ExpoView {
|
|
|
482
489
|
* 将子视图转换为图片
|
|
483
490
|
*/
|
|
484
491
|
private func createImageFromSubviews() -> UIImage? {
|
|
485
|
-
|
|
486
|
-
|
|
492
|
+
let size = resolvedCustomSubviewSize(defaultSize: CGSize(width: 200, height: 60))
|
|
493
|
+
let key = childrenCacheKey(for: size)
|
|
494
|
+
|
|
495
|
+
if let cachedImage = IconBitmapCache.shared.image(forKey: key) {
|
|
487
496
|
return cachedImage
|
|
488
497
|
}
|
|
489
498
|
|
|
490
499
|
guard let firstSubview = subviews.first else {
|
|
491
500
|
return nil
|
|
492
501
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
let height: CGFloat
|
|
497
|
-
|
|
498
|
-
if customViewWidth > 0 {
|
|
499
|
-
width = CGFloat(customViewWidth)
|
|
500
|
-
} else if firstSubview.bounds.size.width > 0 {
|
|
501
|
-
width = firstSubview.bounds.size.width
|
|
502
|
-
} else {
|
|
503
|
-
width = 200 // 默认宽度
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if customViewHeight > 0 {
|
|
507
|
-
height = CGFloat(customViewHeight)
|
|
508
|
-
} else if firstSubview.bounds.size.height > 0 {
|
|
509
|
-
height = firstSubview.bounds.size.height
|
|
510
|
-
} else {
|
|
511
|
-
height = 60 // 默认高度
|
|
502
|
+
|
|
503
|
+
guard size.width > 0, size.height > 0 else {
|
|
504
|
+
return nil
|
|
512
505
|
}
|
|
513
506
|
|
|
514
|
-
let size = CGSize(width: width, height: height)
|
|
515
|
-
|
|
516
507
|
// 强制子视图使用指定尺寸布局
|
|
517
508
|
firstSubview.frame = CGRect(origin: .zero, size: size)
|
|
518
509
|
|
|
@@ -521,6 +512,10 @@ class MarkerView: ExpoView {
|
|
|
521
512
|
forceLayoutRecursively(view: firstSubview)
|
|
522
513
|
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
|
|
523
514
|
}
|
|
515
|
+
|
|
516
|
+
if containsPendingImageContent(in: firstSubview) {
|
|
517
|
+
return nil
|
|
518
|
+
}
|
|
524
519
|
|
|
525
520
|
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
|
526
521
|
defer { UIGraphicsEndImageContext() }
|
|
@@ -538,14 +533,120 @@ class MarkerView: ExpoView {
|
|
|
538
533
|
|
|
539
534
|
|
|
540
535
|
|
|
541
|
-
|
|
542
|
-
if let key = cacheKey {
|
|
543
|
-
IconBitmapCache.shared.setImage(image, forKey: key)
|
|
544
|
-
}
|
|
536
|
+
IconBitmapCache.shared.setImage(image, forKey: key)
|
|
545
537
|
|
|
546
538
|
return image
|
|
547
539
|
}
|
|
548
540
|
|
|
541
|
+
private func resolvedCustomSubviewSize(defaultSize: CGSize) -> CGSize {
|
|
542
|
+
guard let firstSubview = subviews.first else {
|
|
543
|
+
return defaultSize
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if customViewWidth > 0 || customViewHeight > 0 {
|
|
547
|
+
let width = customViewWidth > 0 ? CGFloat(customViewWidth) : defaultSize.width
|
|
548
|
+
let height = customViewHeight > 0 ? CGFloat(customViewHeight) : defaultSize.height
|
|
549
|
+
return CGSize(width: width, height: height)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
forceLayoutRecursively(view: firstSubview)
|
|
553
|
+
|
|
554
|
+
let compressedSize = firstSubview.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
|
|
555
|
+
let fittingSize = firstSubview.sizeThatFits(
|
|
556
|
+
CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
|
|
557
|
+
)
|
|
558
|
+
let intrinsicSize = firstSubview.intrinsicContentSize
|
|
559
|
+
|
|
560
|
+
let width = resolvedDimension(
|
|
561
|
+
candidates: [compressedSize.width, fittingSize.width, intrinsicSize.width, firstSubview.bounds.size.width],
|
|
562
|
+
fallback: defaultSize.width
|
|
563
|
+
)
|
|
564
|
+
let height = resolvedDimension(
|
|
565
|
+
candidates: [compressedSize.height, fittingSize.height, intrinsicSize.height, firstSubview.bounds.size.height],
|
|
566
|
+
fallback: defaultSize.height
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
return CGSize(width: width, height: height)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
private func resolvedDimension(candidates: [CGFloat], fallback: CGFloat) -> CGFloat {
|
|
573
|
+
for value in candidates {
|
|
574
|
+
if value.isFinite && value > 0 {
|
|
575
|
+
return ceil(value)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return fallback
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private func childrenCacheKey(for size: CGSize) -> String {
|
|
583
|
+
let signature = childrenRenderSignature()
|
|
584
|
+
let baseKey = cacheKey.map { "\($0)|\(signature)" }
|
|
585
|
+
?? "children_\(ObjectIdentifier(self).hashValue)|\(signature)"
|
|
586
|
+
let roundedWidth = Int(ceil(size.width))
|
|
587
|
+
let roundedHeight = Int(ceil(size.height))
|
|
588
|
+
return "\(baseKey)|\(roundedWidth)x\(roundedHeight)"
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private func childrenRenderSignature() -> String {
|
|
592
|
+
guard let firstSubview = subviews.first else {
|
|
593
|
+
return "empty"
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
var parts: [String] = []
|
|
597
|
+
|
|
598
|
+
func appendSignature(for view: UIView) {
|
|
599
|
+
parts.append(String(describing: type(of: view)))
|
|
600
|
+
let bounds = view.bounds
|
|
601
|
+
parts.append("b:\(Int(bounds.width.rounded()))x\(Int(bounds.height.rounded()))")
|
|
602
|
+
|
|
603
|
+
if let label = view as? UILabel {
|
|
604
|
+
parts.append("t:\(label.text ?? "")")
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if let imageView = view as? UIImageView,
|
|
608
|
+
let image = imageView.image {
|
|
609
|
+
parts.append("i:\(Int(image.size.width.rounded()))x\(Int(image.size.height.rounded()))")
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
parts.append("c:\(view.subviews.count)")
|
|
613
|
+
for child in view.subviews {
|
|
614
|
+
appendSignature(for: child)
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
appendSignature(for: firstSubview)
|
|
619
|
+
return parts.joined(separator: "|")
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private func hasPendingImageContent() -> Bool {
|
|
623
|
+
guard let firstSubview = subviews.first else {
|
|
624
|
+
return false
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return containsPendingImageContent(in: firstSubview)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
private func containsPendingImageContent(in view: UIView) -> Bool {
|
|
631
|
+
if view.isHidden || view.alpha <= 0 {
|
|
632
|
+
return false
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if let imageView = view as? UIImageView {
|
|
636
|
+
let bounds = imageView.bounds
|
|
637
|
+
let hasSize = bounds.width > 0 || bounds.height > 0
|
|
638
|
+
if hasSize && imageView.image == nil {
|
|
639
|
+
return true
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
for subview in view.subviews where containsPendingImageContent(in: subview) {
|
|
644
|
+
return true
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return false
|
|
648
|
+
}
|
|
649
|
+
|
|
549
650
|
|
|
550
651
|
/**
|
|
551
652
|
* 递归强制布局视图及其所有子视图
|
|
@@ -580,6 +681,7 @@ class MarkerView: ExpoView {
|
|
|
580
681
|
isRemoving = true
|
|
581
682
|
pendingAddTask?.cancel(); pendingAddTask = nil
|
|
582
683
|
pendingUpdateTask?.cancel(); pendingUpdateTask = nil
|
|
684
|
+
pendingSubviewRefreshTask?.cancel(); pendingSubviewRefreshTask = nil
|
|
583
685
|
|
|
584
686
|
guard let mapView = mapView else {
|
|
585
687
|
isRemoving = false
|
|
@@ -621,19 +723,7 @@ class MarkerView: ExpoView {
|
|
|
621
723
|
return
|
|
622
724
|
}
|
|
623
725
|
|
|
624
|
-
|
|
625
|
-
if self.subviews.count <= 1 {
|
|
626
|
-
// 所有子视图已移除,刷新以恢复默认图标
|
|
627
|
-
if let mapView = mapView, let annotation = annotation {
|
|
628
|
-
DispatchQueue.main.async { [weak self] in
|
|
629
|
-
guard let self = self, !self.isRemoving else {
|
|
630
|
-
return
|
|
631
|
-
}
|
|
632
|
-
mapView.removeAnnotation(annotation)
|
|
633
|
-
mapView.addAnnotation(annotation)
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
}
|
|
726
|
+
scheduleSubviewRefresh(allowFallbackToDefault: true)
|
|
637
727
|
}
|
|
638
728
|
|
|
639
729
|
override func didAddSubview(_ subview: UIView) {
|
|
@@ -644,14 +734,78 @@ class MarkerView: ExpoView {
|
|
|
644
734
|
return
|
|
645
735
|
}
|
|
646
736
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
737
|
+
scheduleSubviewRefresh(allowFallbackToDefault: false)
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
private func scheduleSubviewRefresh(allowFallbackToDefault: Bool) {
|
|
741
|
+
pendingSubviewRefreshTask?.cancel()
|
|
742
|
+
|
|
743
|
+
let task = DispatchWorkItem { [weak self] in
|
|
744
|
+
guard let self = self, !self.isRemoving else { return }
|
|
745
|
+
self.refreshAnnotationForSubviewChanges(allowFallbackToDefault: allowFallbackToDefault)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
pendingSubviewRefreshTask = task
|
|
749
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02, execute: task)
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
private func refreshAnnotationForSubviewChanges(allowFallbackToDefault: Bool) {
|
|
753
|
+
guard let mapView = mapView else { return }
|
|
754
|
+
|
|
755
|
+
if annotation == nil {
|
|
756
|
+
updateAnnotation()
|
|
757
|
+
return
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
guard let annotation = annotation else { return }
|
|
761
|
+
|
|
762
|
+
if subviews.isEmpty {
|
|
763
|
+
if allowFallbackToDefault {
|
|
764
|
+
lastRenderedChildrenSignature = nil
|
|
765
|
+
annotationView = nil
|
|
766
|
+
mapView.removeAnnotation(annotation)
|
|
767
|
+
mapView.addAnnotation(annotation)
|
|
768
|
+
}
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
let signature = childrenRenderSignature()
|
|
773
|
+
if signature == lastRenderedChildrenSignature, annotationView?.image != nil {
|
|
774
|
+
return
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
invalidateCurrentChildrenCache()
|
|
778
|
+
|
|
779
|
+
if annotationView is MAPinAnnotationView {
|
|
780
|
+
annotationView = nil
|
|
650
781
|
mapView.removeAnnotation(annotation)
|
|
651
782
|
mapView.addAnnotation(annotation)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
783
|
+
return
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
guard let annotationView = annotationView else {
|
|
787
|
+
mapView.removeAnnotation(annotation)
|
|
788
|
+
mapView.addAnnotation(annotation)
|
|
789
|
+
return
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if let image = createImageFromSubviews() {
|
|
793
|
+
annotationView.image = image
|
|
794
|
+
applyCenterOffset(to: annotationView, defaultOffset: .zero)
|
|
795
|
+
annotationView.canShowCallout = false
|
|
796
|
+
annotationView.isDraggable = draggable
|
|
797
|
+
lastRenderedChildrenSignature = signature
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
private func invalidateCurrentChildrenCache() {
|
|
802
|
+
let sizes = [
|
|
803
|
+
resolvedCustomSubviewSize(defaultSize: CGSize(width: 200, height: 40)),
|
|
804
|
+
resolvedCustomSubviewSize(defaultSize: CGSize(width: 200, height: 60))
|
|
805
|
+
]
|
|
806
|
+
|
|
807
|
+
for size in sizes {
|
|
808
|
+
IconBitmapCache.shared.removeImage(forKey: childrenCacheKey(for: size))
|
|
655
809
|
}
|
|
656
810
|
}
|
|
657
811
|
|
|
@@ -68,7 +68,7 @@ public class MarkerViewModule: Module {
|
|
|
68
68
|
Prop("growAnimation") { (view: MarkerView, enabled: Bool) in
|
|
69
69
|
view.growAnimation = enabled
|
|
70
70
|
}
|
|
71
|
-
Prop("cacheKey") { (view: MarkerView, key: String) in
|
|
71
|
+
Prop("cacheKey") { (view: MarkerView, key: String?) in
|
|
72
72
|
view.setCacheKey(key)
|
|
73
73
|
}
|
|
74
74
|
|