@lugg/maps 1.0.0-beta.0 → 1.0.0-beta.2

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 (60) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +74 -0
  4. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +38 -0
  5. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +0 -11
  6. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +73 -51
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +8 -1
  8. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +181 -5
  9. package/ios/LuggCalloutView.h +23 -0
  10. package/ios/LuggCalloutView.mm +145 -0
  11. package/ios/LuggMarkerView.h +2 -0
  12. package/ios/LuggMarkerView.mm +17 -2
  13. package/ios/core/AppleMapProvider.mm +211 -24
  14. package/ios/core/GoogleMapProvider.mm +125 -5
  15. package/ios/core/LuggAnnotationView.h +12 -0
  16. package/ios/core/LuggAnnotationView.mm +55 -0
  17. package/lib/module/MapProvider.web.js +3 -1
  18. package/lib/module/MapProvider.web.js.map +1 -1
  19. package/lib/module/MapView.web.js +15 -2
  20. package/lib/module/MapView.web.js.map +1 -1
  21. package/lib/module/components/Marker.js +21 -4
  22. package/lib/module/components/Marker.js.map +1 -1
  23. package/lib/module/components/Marker.web.js +86 -22
  24. package/lib/module/components/Marker.web.js.map +1 -1
  25. package/lib/module/components/index.js.map +1 -1
  26. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/typescript/src/MapProvider.web.d.ts +6 -0
  29. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  30. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  31. package/lib/typescript/src/components/GeoJson.types.d.ts +11 -1
  32. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -1
  33. package/lib/typescript/src/components/Marker.d.ts +1 -1
  34. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  35. package/lib/typescript/src/components/Marker.types.d.ts +27 -1
  36. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  37. package/lib/typescript/src/components/Marker.web.d.ts +1 -1
  38. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  39. package/lib/typescript/src/components/index.d.ts +1 -1
  40. package/lib/typescript/src/components/index.d.ts.map +1 -1
  41. package/lib/typescript/src/components/index.web.d.ts +1 -1
  42. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  43. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +13 -0
  44. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts.map +1 -0
  45. package/lib/typescript/src/index.d.ts +2 -2
  46. package/lib/typescript/src/index.d.ts.map +1 -1
  47. package/lib/typescript/src/index.web.d.ts +2 -2
  48. package/lib/typescript/src/index.web.d.ts.map +1 -1
  49. package/package.json +2 -1
  50. package/src/MapProvider.web.tsx +11 -1
  51. package/src/MapView.web.tsx +26 -2
  52. package/src/components/GeoJson.types.ts +12 -1
  53. package/src/components/Marker.tsx +29 -1
  54. package/src/components/Marker.types.ts +28 -1
  55. package/src/components/Marker.web.tsx +113 -23
  56. package/src/components/index.ts +1 -0
  57. package/src/components/index.web.ts +1 -1
  58. package/src/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  59. package/src/index.ts +2 -0
  60. package/src/index.web.ts +2 -1
@@ -0,0 +1,23 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @class LuggCalloutView;
7
+
8
+ @protocol LuggCalloutViewDelegate <NSObject>
9
+ @optional
10
+ - (void)calloutViewDidUpdate:(LuggCalloutView *)calloutView;
11
+ @end
12
+
13
+ @interface LuggCalloutView : RCTViewComponentView
14
+
15
+ @property(nonatomic, readonly) BOOL bubbled;
16
+ @property(nonatomic, readonly) CGPoint anchor;
17
+ @property(nonatomic, readonly) BOOL hasCustomContent;
18
+ @property(nonatomic, readonly) UIView *contentView;
19
+ @property(nonatomic, weak, nullable) id<LuggCalloutViewDelegate> delegate;
20
+
21
+ @end
22
+
23
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,145 @@
1
+ #import "LuggCalloutView.h"
2
+
3
+ #import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/RNMapsSpec/Props.h>
5
+ #import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
6
+
7
+ #import "RCTFabricComponentsPlugins.h"
8
+
9
+ using namespace facebook::react;
10
+
11
+ @interface LuggCalloutContentView : UIView
12
+ - (void)updateContentSize;
13
+ @end
14
+
15
+ @implementation LuggCalloutContentView
16
+
17
+ - (CGSize)intrinsicContentSize {
18
+ CGFloat width = 0;
19
+ CGFloat height = 0;
20
+ for (UIView *subview in self.subviews) {
21
+ width = MAX(width, CGRectGetMaxX(subview.frame));
22
+ height = MAX(height, CGRectGetMaxY(subview.frame));
23
+ }
24
+ if (width > 0 && height > 0) {
25
+ return CGSizeMake(width, height);
26
+ }
27
+ return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
28
+ }
29
+
30
+ - (void)updateContentSize {
31
+ CGSize size = [self intrinsicContentSize];
32
+ if (size.width > 0 && size.height > 0) {
33
+ self.frame = CGRectMake(0, 0, size.width, size.height);
34
+ }
35
+ [self invalidateIntrinsicContentSize];
36
+ }
37
+
38
+ @end
39
+
40
+ @interface LuggCalloutView () <RCTLuggCalloutViewViewProtocol>
41
+ @end
42
+
43
+ @implementation LuggCalloutView {
44
+ LuggCalloutContentView *_contentView;
45
+ BOOL _bubbled;
46
+ CGPoint _anchor;
47
+ }
48
+
49
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
50
+ return concreteComponentDescriptorProvider<
51
+ LuggCalloutViewComponentDescriptor>();
52
+ }
53
+
54
+ - (instancetype)initWithFrame:(CGRect)frame {
55
+ if (self = [super initWithFrame:frame]) {
56
+ static const auto defaultProps =
57
+ std::make_shared<const LuggCalloutViewProps>();
58
+ _props = defaultProps;
59
+
60
+ _bubbled = YES;
61
+ _anchor = CGPointMake(0.5, 1.0);
62
+ _contentView = [[LuggCalloutContentView alloc] init];
63
+ _contentView.backgroundColor = [UIColor clearColor];
64
+
65
+ self.backgroundColor = [UIColor clearColor];
66
+ self.hidden = YES;
67
+ }
68
+
69
+ return self;
70
+ }
71
+
72
+ - (void)updateProps:(const Props::Shared &)props
73
+ oldProps:(const Props::Shared &)oldProps {
74
+ const auto &newViewProps =
75
+ *std::static_pointer_cast<LuggCalloutViewProps const>(props);
76
+
77
+ _bubbled = newViewProps.bubbled;
78
+
79
+ if (oldProps) {
80
+ const auto &oldViewProps =
81
+ *std::static_pointer_cast<LuggCalloutViewProps const>(oldProps);
82
+ if (newViewProps.anchor.x != oldViewProps.anchor.x ||
83
+ newViewProps.anchor.y != oldViewProps.anchor.y) {
84
+ _anchor = CGPointMake(newViewProps.anchor.x, newViewProps.anchor.y);
85
+ }
86
+ } else if (newViewProps.anchor.x != 0 || newViewProps.anchor.y != 0) {
87
+ _anchor = CGPointMake(newViewProps.anchor.x, newViewProps.anchor.y);
88
+ }
89
+
90
+ [super updateProps:props oldProps:oldProps];
91
+ [_delegate calloutViewDidUpdate:self];
92
+ }
93
+
94
+ - (BOOL)bubbled {
95
+ return _bubbled;
96
+ }
97
+
98
+ - (CGPoint)anchor {
99
+ return _anchor;
100
+ }
101
+
102
+ - (void)mountChildComponentView:
103
+ (UIView<RCTComponentViewProtocol> *)childComponentView
104
+ index:(NSInteger)index {
105
+ [_contentView insertSubview:childComponentView atIndex:index];
106
+ [_contentView updateContentSize];
107
+ }
108
+
109
+ - (void)unmountChildComponentView:
110
+ (UIView<RCTComponentViewProtocol> *)childComponentView
111
+ index:(NSInteger)index {
112
+ [childComponentView removeFromSuperview];
113
+ [_contentView updateContentSize];
114
+ }
115
+
116
+ - (void)layoutSubviews {
117
+ [super layoutSubviews];
118
+ CGSize oldSize = _contentView.bounds.size;
119
+ [_contentView updateContentSize];
120
+ CGSize newSize = _contentView.bounds.size;
121
+ if (!CGSizeEqualToSize(oldSize, newSize)) {
122
+ [_delegate calloutViewDidUpdate:self];
123
+ }
124
+ }
125
+
126
+ - (BOOL)hasCustomContent {
127
+ return _contentView.subviews.count > 0;
128
+ }
129
+
130
+ - (UIView *)contentView {
131
+ return _contentView;
132
+ }
133
+
134
+ - (void)prepareForRecycle {
135
+ [super prepareForRecycle];
136
+ for (UIView *subview in _contentView.subviews) {
137
+ [subview removeFromSuperview];
138
+ }
139
+ }
140
+
141
+ Class<RCTComponentViewProtocol> LuggCalloutViewCls(void) {
142
+ return LuggCalloutView.class;
143
+ }
144
+
145
+ @end
@@ -5,6 +5,7 @@
5
5
  NS_ASSUME_NONNULL_BEGIN
6
6
 
7
7
  @class LuggMarkerView;
8
+ @class LuggCalloutView;
8
9
 
9
10
  @protocol LuggMarkerViewDelegate <NSObject>
10
11
  @optional
@@ -27,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
27
28
  @property(nonatomic, readonly) BOOL hasCustomView;
28
29
  @property(nonatomic, readonly) BOOL didLayout;
29
30
  @property(nonatomic, readonly) UIView *iconView;
31
+ @property(nonatomic, readonly, nullable) LuggCalloutView *calloutView;
30
32
  @property(nonatomic, weak, nullable) id<LuggMarkerViewDelegate> delegate;
31
33
  @property(nonatomic, strong, nullable) NSObject *marker;
32
34
 
@@ -1,4 +1,5 @@
1
1
  #import "LuggMarkerView.h"
2
+ #import "LuggCalloutView.h"
2
3
  #import "events/MarkerDragEvent.h"
3
4
  #import "events/MarkerPressEvent.h"
4
5
 
@@ -28,6 +29,7 @@ using namespace luggmaps::events;
28
29
  BOOL _draggable;
29
30
  BOOL _didLayout;
30
31
  UIView *_iconView;
32
+ LuggCalloutView *_calloutView;
31
33
  }
32
34
 
33
35
  + (ComponentDescriptorProvider)componentDescriptorProvider {
@@ -92,14 +94,22 @@ using namespace luggmaps::events;
92
94
  - (void)mountChildComponentView:
93
95
  (UIView<RCTComponentViewProtocol> *)childComponentView
94
96
  index:(NSInteger)index {
95
- [_iconView insertSubview:childComponentView atIndex:index];
97
+ if ([childComponentView isKindOfClass:[LuggCalloutView class]]) {
98
+ _calloutView = (LuggCalloutView *)childComponentView;
99
+ } else {
100
+ [_iconView insertSubview:childComponentView atIndex:index];
101
+ }
96
102
  _didLayout = NO;
97
103
  }
98
104
 
99
105
  - (void)unmountChildComponentView:
100
106
  (UIView<RCTComponentViewProtocol> *)childComponentView
101
107
  index:(NSInteger)index {
102
- [childComponentView removeFromSuperview];
108
+ if ([childComponentView isKindOfClass:[LuggCalloutView class]]) {
109
+ _calloutView = nil;
110
+ } else {
111
+ [childComponentView removeFromSuperview];
112
+ }
103
113
  _didLayout = NO;
104
114
  }
105
115
 
@@ -180,6 +190,10 @@ using namespace luggmaps::events;
180
190
  return _iconView;
181
191
  }
182
192
 
193
+ - (LuggCalloutView *)calloutView {
194
+ return _calloutView;
195
+ }
196
+
183
197
  - (UIImage *)createIconImage {
184
198
  CGSize size = _iconView.bounds.size;
185
199
  if (size.width <= 0 || size.height <= 0) {
@@ -271,6 +285,7 @@ using namespace luggmaps::events;
271
285
  - (void)prepareForRecycle {
272
286
  [super prepareForRecycle];
273
287
  _didLayout = NO;
288
+ _calloutView = nil;
274
289
  self.marker = nil;
275
290
  self.delegate = nil;
276
291
  [self resetIconViewTransform];
@@ -1,8 +1,10 @@
1
1
  #import "AppleMapProvider.h"
2
+ #import "../LuggCalloutView.h"
2
3
  #import "../LuggMarkerView.h"
3
4
  #import "../LuggPolygonView.h"
4
5
  #import "../LuggPolylineView.h"
5
6
  #import "../extensions/MKMapView+Zoom.h"
7
+ #import "LuggAnnotationView.h"
6
8
  #import "MKPolylineAnimator.h"
7
9
 
8
10
  @interface AppleMarkerAnnotation : NSObject <MKAnnotation>
@@ -11,6 +13,7 @@
11
13
  @property(nonatomic, copy, nullable) NSString *subtitle;
12
14
  @property(nonatomic, strong) LuggMarkerView *markerView;
13
15
  @property(nonatomic, weak) MKAnnotationView *annotationView;
16
+ @property(nonatomic, copy, nullable) dispatch_block_t pendingScaleAnimation;
14
17
  @end
15
18
 
16
19
  @implementation AppleMarkerAnnotation
@@ -20,11 +23,12 @@
20
23
  @end
21
24
 
22
25
  @interface AppleMapProvider () <
23
- LuggMarkerViewDelegate, LuggPolylineViewDelegate, LuggPolygonViewDelegate,
24
- UIGestureRecognizerDelegate>
26
+ LuggMarkerViewDelegate, LuggCalloutViewDelegate, LuggPolylineViewDelegate,
27
+ LuggPolygonViewDelegate, UIGestureRecognizerDelegate>
25
28
  @end
26
29
 
27
30
  @implementation AppleMapProvider {
31
+ UIView *_wrapperView;
28
32
  LuggAppleMapViewContent *_mapView;
29
33
  BOOL _isMapReady;
30
34
  BOOL _isDragging;
@@ -34,6 +38,8 @@
34
38
  NSMapTable<id<MKOverlay>, LuggPolygonView *> *_overlayToPolygonMap;
35
39
  UITapGestureRecognizer *_tapGesture;
36
40
  UILongPressGestureRecognizer *_longPressGesture;
41
+ LuggMarkerView *_activeNonBubbledMarker;
42
+ BOOL _isReselectingAnnotation;
37
43
  // Edge insets animation
38
44
  CADisplayLink *_edgeInsetsDisplayLink;
39
45
  UIEdgeInsets _edgeInsetsFrom;
@@ -91,6 +97,7 @@
91
97
 
92
98
  [_tapGesture requireGestureRecognizerToFail:_longPressGesture];
93
99
 
100
+ _wrapperView = wrapperView;
94
101
  [wrapperView addSubview:_mapView];
95
102
 
96
103
  MKCoordinateRegion region = [_mapView regionForCenterCoordinate:coordinate
@@ -103,6 +110,7 @@
103
110
  }
104
111
 
105
112
  - (void)destroy {
113
+ [self dismissNonBubbledCallout];
106
114
  [self stopEdgeInsetsAnimation];
107
115
  if (_tapGesture) {
108
116
  [_mapView removeGestureRecognizer:_tapGesture];
@@ -319,11 +327,24 @@
319
327
  return NO;
320
328
  }
321
329
 
330
+ - (BOOL)hitTestCalloutAtPoint:(CGPoint)point {
331
+ if (!_activeNonBubbledMarker)
332
+ return NO;
333
+
334
+ UIView *contentView = _activeNonBubbledMarker.calloutView.contentView;
335
+ CGPoint local = [contentView convertPoint:point fromView:_mapView];
336
+ return [contentView pointInside:local withEvent:nil];
337
+ }
338
+
322
339
  - (void)handleTap:(UITapGestureRecognizer *)gesture {
323
340
  if (gesture.state != UIGestureRecognizerStateEnded)
324
341
  return;
325
342
 
326
343
  CGPoint point = [gesture locationInView:_mapView];
344
+ if ([self hitTestCalloutAtPoint:point])
345
+ return;
346
+
347
+ [self dismissNonBubbledCallout];
327
348
 
328
349
  if ([self hitTestAnnotationAtPoint:point])
329
350
  return;
@@ -390,6 +411,7 @@
390
411
  longitude:mapView.centerCoordinate.longitude
391
412
  zoom:mapView.zoomLevel
392
413
  gesture:_isDragging];
414
+ [self positionNonBubbledCallout];
393
415
  }
394
416
 
395
417
  - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
@@ -423,22 +445,23 @@
423
445
  }
424
446
 
425
447
  if (!markerView.hasCustomView) {
426
- MKMarkerAnnotationView *markerAnnotationView =
427
- [[MKMarkerAnnotationView alloc] initWithAnnotation:annotation
428
- reuseIdentifier:nil];
448
+ LuggMarkerAnnotationView *markerAnnotationView =
449
+ [[LuggMarkerAnnotationView alloc] initWithAnnotation:annotation
450
+ reuseIdentifier:nil];
429
451
  markerAnnotationView.canShowCallout = YES;
430
452
  markerAnnotationView.displayPriority = MKFeatureDisplayPriorityRequired;
431
453
  markerAnnotationView.layer.zPosition = markerView.zIndex;
432
454
  markerAnnotationView.zPriority = markerView.zIndex;
433
455
  markerAnnotationView.draggable = markerView.draggable;
456
+ [self applyCalloutView:markerView annotationView:markerAnnotationView];
434
457
  [self addCenterTapGesture:markerAnnotationView];
435
458
  markerAnnotation.annotationView = markerAnnotationView;
436
459
  return markerAnnotationView;
437
460
  }
438
461
 
439
- MKAnnotationView *annotationView =
440
- [[MKAnnotationView alloc] initWithAnnotation:annotation
441
- reuseIdentifier:nil];
462
+ LuggAnnotationView *annotationView =
463
+ [[LuggAnnotationView alloc] initWithAnnotation:annotation
464
+ reuseIdentifier:nil];
442
465
  annotationView.canShowCallout = YES;
443
466
  annotationView.displayPriority = MKFeatureDisplayPriorityRequired;
444
467
  annotationView.layer.zPosition = markerView.zIndex;
@@ -446,6 +469,8 @@
446
469
  annotationView.draggable = markerView.draggable;
447
470
  [self addCenterTapGesture:annotationView];
448
471
 
472
+ [self applyCalloutView:markerView annotationView:annotationView];
473
+
449
474
  if (!markerView.rasterize) {
450
475
  UIView *iconView = markerView.iconView;
451
476
  [iconView removeFromSuperview];
@@ -516,6 +541,108 @@
516
541
  CGPoint point = [_mapView convertCoordinate:markerView.coordinate
517
542
  toPointToView:_mapView];
518
543
  [markerView emitPressEventWithPoint:point];
544
+
545
+ LuggCalloutView *calloutView = markerView.calloutView;
546
+ if (calloutView && !calloutView.bubbled && calloutView.hasCustomContent) {
547
+ [self showNonBubbledCallout:markerView];
548
+ }
549
+ }
550
+ }
551
+
552
+ - (void)mapView:(MKMapView *)mapView
553
+ didDeselectAnnotationView:(MKAnnotationView *)view {
554
+ if (![view.annotation isKindOfClass:[AppleMarkerAnnotation class]])
555
+ return;
556
+
557
+ AppleMarkerAnnotation *annotation = (AppleMarkerAnnotation *)view.annotation;
558
+ LuggMarkerView *markerView = annotation.markerView;
559
+
560
+ if (markerView && _activeNonBubbledMarker == markerView &&
561
+ !_isReselectingAnnotation) {
562
+ _isReselectingAnnotation = YES;
563
+ [_mapView selectAnnotation:annotation animated:NO];
564
+ _isReselectingAnnotation = NO;
565
+ }
566
+ }
567
+
568
+ - (void)showNonBubbledCallout:(LuggMarkerView *)markerView {
569
+ [self dismissNonBubbledCallout];
570
+
571
+ LuggCalloutView *calloutView = markerView.calloutView;
572
+ UIView *contentView = calloutView.contentView;
573
+ [contentView removeFromSuperview];
574
+
575
+ contentView.userInteractionEnabled = YES;
576
+ contentView.hidden = YES;
577
+ calloutView.delegate = self;
578
+
579
+ AppleMarkerAnnotation *annotation =
580
+ (AppleMarkerAnnotation *)markerView.marker;
581
+ MKAnnotationView *annotationView = annotation.annotationView;
582
+ if (annotationView) {
583
+ annotationView.clipsToBounds = NO;
584
+ [annotationView addSubview:contentView];
585
+ } else {
586
+ [_wrapperView addSubview:contentView];
587
+ }
588
+
589
+ _activeNonBubbledMarker = markerView;
590
+ dispatch_async(dispatch_get_main_queue(), ^{
591
+ [self positionNonBubbledCallout];
592
+ contentView.hidden = NO;
593
+ });
594
+ }
595
+
596
+ - (void)calloutViewDidUpdate:(LuggCalloutView *)calloutView {
597
+ [self positionNonBubbledCallout];
598
+ }
599
+
600
+ - (void)positionNonBubbledCallout {
601
+ if (!_activeNonBubbledMarker)
602
+ return;
603
+
604
+ LuggCalloutView *calloutView = _activeNonBubbledMarker.calloutView;
605
+ UIView *contentView = calloutView.contentView;
606
+ CGSize contentSize = contentView.bounds.size;
607
+ if (contentSize.width <= 0 || contentSize.height <= 0)
608
+ return;
609
+
610
+ CGPoint anchor = calloutView.anchor;
611
+
612
+ AppleMarkerAnnotation *annotation =
613
+ (AppleMarkerAnnotation *)_activeNonBubbledMarker.marker;
614
+ MKAnnotationView *annotationView = annotation.annotationView;
615
+
616
+ if (annotationView && contentView.superview == annotationView) {
617
+ CGPoint center = CGPointMake(annotationView.bounds.size.width / 2.0 +
618
+ contentSize.width * (0.5 - anchor.x),
619
+ annotationView.bounds.size.height / 2.0 +
620
+ contentSize.height * (0.5 - anchor.y));
621
+ contentView.center = center;
622
+ } else {
623
+ CGPoint point =
624
+ [_mapView convertCoordinate:_activeNonBubbledMarker.coordinate
625
+ toPointToView:_wrapperView];
626
+ contentView.center =
627
+ CGPointMake(point.x + contentSize.width * (0.5 - anchor.x),
628
+ point.y + contentSize.height * (0.5 - anchor.y));
629
+ }
630
+ }
631
+
632
+ - (void)dismissNonBubbledCallout {
633
+ if (!_activeNonBubbledMarker)
634
+ return;
635
+
636
+ LuggMarkerView *markerView = _activeNonBubbledMarker;
637
+ _activeNonBubbledMarker = nil;
638
+
639
+ markerView.calloutView.delegate = nil;
640
+ [markerView.calloutView.contentView removeFromSuperview];
641
+
642
+ AppleMarkerAnnotation *annotation =
643
+ (AppleMarkerAnnotation *)markerView.marker;
644
+ if (annotation) {
645
+ [_mapView deselectAnnotation:annotation animated:NO];
519
646
  }
520
647
  }
521
648
 
@@ -574,6 +701,27 @@
574
701
  }
575
702
  }
576
703
 
704
+ - (void)applyCalloutView:(LuggMarkerView *)markerView
705
+ annotationView:(MKAnnotationView *)annotationView {
706
+ LuggCalloutView *calloutView = markerView.calloutView;
707
+ if (!calloutView) {
708
+ annotationView.detailCalloutAccessoryView = nil;
709
+ annotationView.rightCalloutAccessoryView = nil;
710
+ return;
711
+ }
712
+
713
+ annotationView.rightCalloutAccessoryView = nil;
714
+
715
+ if (!calloutView.bubbled) {
716
+ annotationView.canShowCallout = NO;
717
+ annotationView.detailCalloutAccessoryView = nil;
718
+ return;
719
+ }
720
+
721
+ annotationView.detailCalloutAccessoryView =
722
+ calloutView.hasCustomContent ? calloutView.contentView : nil;
723
+ }
724
+
577
725
  #pragma mark - MarkerViewDelegate
578
726
 
579
727
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
@@ -600,6 +748,7 @@
600
748
  annotationView.draggable = markerView.draggable;
601
749
  annotationView.layer.zPosition = markerView.zIndex;
602
750
  annotationView.zPriority = markerView.zIndex;
751
+ [self applyCalloutView:markerView annotationView:annotationView];
603
752
  }
604
753
 
605
754
  [self updateAnnotationViewFrame:annotation];
@@ -640,6 +789,10 @@
640
789
  (AppleMarkerAnnotation *)markerView.marker;
641
790
 
642
791
  if (annotation) {
792
+ if (annotation.pendingScaleAnimation) {
793
+ dispatch_block_cancel(annotation.pendingScaleAnimation);
794
+ annotation.pendingScaleAnimation = nil;
795
+ }
643
796
  annotation.annotationView.transform = CGAffineTransformIdentity;
644
797
  annotation.markerView = nil;
645
798
  annotation.annotationView = nil;
@@ -658,31 +811,65 @@
658
811
  annotationView.transform = CGAffineTransformIdentity;
659
812
 
660
813
  UIView *iconView = markerView.iconView;
661
- CGRect frame = iconView.frame;
662
- if (frame.size.width <= 0 || frame.size.height <= 0)
814
+ iconView.transform = CGAffineTransformIdentity;
815
+
816
+ // Clean up iconView from in-progress rasterize animation
817
+ if (markerView.rasterize && [iconView superview] == annotationView) {
818
+ [iconView removeFromSuperview];
819
+ iconView.layer.anchorPoint = CGPointMake(0.5, 0.5);
820
+ }
821
+
822
+ CGSize size = iconView.bounds.size;
823
+ if (size.width <= 0 || size.height <= 0)
663
824
  return;
664
825
 
665
826
  CGFloat scale = markerView.scale;
666
827
  CGPoint anchor = markerView.anchor;
828
+ CGFloat scaledWidth = size.width * scale;
829
+ CGFloat scaledHeight = size.height * scale;
667
830
 
668
- if (markerView.rasterize) {
669
- annotationView.image = [markerView createScaledIconImage];
670
- } else {
671
- iconView.layer.anchorPoint = anchor;
672
- iconView.transform = CGAffineTransformMakeScale(scale, scale);
673
- iconView.frame =
674
- CGRectMake(frame.size.width * (0.5 - anchor.x) * (scale - 1),
675
- frame.size.height * (0.5 - anchor.y) * (scale - 1),
676
- frame.size.width, frame.size.height);
831
+ AppleMarkerAnnotation *annotation =
832
+ (AppleMarkerAnnotation *)markerView.marker;
833
+
834
+ // Cancel any pending rasterize
835
+ if (annotation.pendingScaleAnimation) {
836
+ dispatch_block_cancel(annotation.pendingScaleAnimation);
837
+ annotation.pendingScaleAnimation = nil;
838
+ }
839
+
840
+ // Use live iconView during rapid updates (skip expensive rasterize per frame)
841
+ if (markerView.rasterize && [iconView superview] != annotationView) {
842
+ annotationView.image = nil;
843
+ [annotationView addSubview:iconView];
677
844
  }
678
845
 
679
- annotationView.bounds =
680
- CGRectMake(0, 0, frame.size.width * scale, frame.size.height * scale);
681
- annotationView.centerOffset =
682
- CGPointMake(frame.size.width * scale * (anchor.x - 0.5),
683
- -frame.size.height * scale * (anchor.y - 0.5));
846
+ iconView.layer.anchorPoint = anchor;
847
+ iconView.bounds = CGRectMake(0, 0, size.width, size.height);
848
+ iconView.center =
849
+ CGPointMake(scaledWidth * anchor.x, scaledHeight * anchor.y);
850
+ iconView.transform = CGAffineTransformMakeScale(scale, scale);
851
+
852
+ annotationView.bounds = CGRectMake(0, 0, scaledWidth, scaledHeight);
853
+ annotationView.centerOffset = CGPointMake(scaledWidth * (0.5 - anchor.x),
854
+ scaledHeight * (0.5 - anchor.y));
684
855
  annotationView.transform =
685
856
  CGAffineTransformMakeRotation(markerView.rotate * M_PI / 180.0);
857
+
858
+ // Debounce: rasterize once updates settle
859
+ if (markerView.rasterize) {
860
+ dispatch_block_t rasterizeBlock =
861
+ dispatch_block_create((dispatch_block_flags_t)0, ^{
862
+ annotation.pendingScaleAnimation = nil;
863
+ annotationView.image = [markerView createScaledIconImage];
864
+ [iconView removeFromSuperview];
865
+ [markerView resetIconViewTransform];
866
+ });
867
+
868
+ annotation.pendingScaleAnimation = rasterizeBlock;
869
+ dispatch_after(
870
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(150 * NSEC_PER_MSEC)),
871
+ dispatch_get_main_queue(), rasterizeBlock);
872
+ }
686
873
  }
687
874
 
688
875
  - (void)updateAnnotationViewFrame:(AppleMarkerAnnotation *)annotation {