@lugg/maps 1.0.0-beta.0 → 1.0.0-beta.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 (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 +152 -8
  14. package/ios/core/GoogleMapProvider.mm +114 -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>
@@ -20,11 +22,12 @@
20
22
  @end
21
23
 
22
24
  @interface AppleMapProvider () <
23
- LuggMarkerViewDelegate, LuggPolylineViewDelegate, LuggPolygonViewDelegate,
24
- UIGestureRecognizerDelegate>
25
+ LuggMarkerViewDelegate, LuggCalloutViewDelegate, LuggPolylineViewDelegate,
26
+ LuggPolygonViewDelegate, UIGestureRecognizerDelegate>
25
27
  @end
26
28
 
27
29
  @implementation AppleMapProvider {
30
+ UIView *_wrapperView;
28
31
  LuggAppleMapViewContent *_mapView;
29
32
  BOOL _isMapReady;
30
33
  BOOL _isDragging;
@@ -34,6 +37,8 @@
34
37
  NSMapTable<id<MKOverlay>, LuggPolygonView *> *_overlayToPolygonMap;
35
38
  UITapGestureRecognizer *_tapGesture;
36
39
  UILongPressGestureRecognizer *_longPressGesture;
40
+ LuggMarkerView *_activeNonBubbledMarker;
41
+ BOOL _isReselectingAnnotation;
37
42
  // Edge insets animation
38
43
  CADisplayLink *_edgeInsetsDisplayLink;
39
44
  UIEdgeInsets _edgeInsetsFrom;
@@ -91,6 +96,7 @@
91
96
 
92
97
  [_tapGesture requireGestureRecognizerToFail:_longPressGesture];
93
98
 
99
+ _wrapperView = wrapperView;
94
100
  [wrapperView addSubview:_mapView];
95
101
 
96
102
  MKCoordinateRegion region = [_mapView regionForCenterCoordinate:coordinate
@@ -103,6 +109,7 @@
103
109
  }
104
110
 
105
111
  - (void)destroy {
112
+ [self dismissNonBubbledCallout];
106
113
  [self stopEdgeInsetsAnimation];
107
114
  if (_tapGesture) {
108
115
  [_mapView removeGestureRecognizer:_tapGesture];
@@ -319,11 +326,24 @@
319
326
  return NO;
320
327
  }
321
328
 
329
+ - (BOOL)hitTestCalloutAtPoint:(CGPoint)point {
330
+ if (!_activeNonBubbledMarker)
331
+ return NO;
332
+
333
+ UIView *contentView = _activeNonBubbledMarker.calloutView.contentView;
334
+ CGPoint local = [contentView convertPoint:point fromView:_mapView];
335
+ return [contentView pointInside:local withEvent:nil];
336
+ }
337
+
322
338
  - (void)handleTap:(UITapGestureRecognizer *)gesture {
323
339
  if (gesture.state != UIGestureRecognizerStateEnded)
324
340
  return;
325
341
 
326
342
  CGPoint point = [gesture locationInView:_mapView];
343
+ if ([self hitTestCalloutAtPoint:point])
344
+ return;
345
+
346
+ [self dismissNonBubbledCallout];
327
347
 
328
348
  if ([self hitTestAnnotationAtPoint:point])
329
349
  return;
@@ -390,6 +410,7 @@
390
410
  longitude:mapView.centerCoordinate.longitude
391
411
  zoom:mapView.zoomLevel
392
412
  gesture:_isDragging];
413
+ [self positionNonBubbledCallout];
393
414
  }
394
415
 
395
416
  - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
@@ -423,22 +444,23 @@
423
444
  }
424
445
 
425
446
  if (!markerView.hasCustomView) {
426
- MKMarkerAnnotationView *markerAnnotationView =
427
- [[MKMarkerAnnotationView alloc] initWithAnnotation:annotation
428
- reuseIdentifier:nil];
447
+ LuggMarkerAnnotationView *markerAnnotationView =
448
+ [[LuggMarkerAnnotationView alloc] initWithAnnotation:annotation
449
+ reuseIdentifier:nil];
429
450
  markerAnnotationView.canShowCallout = YES;
430
451
  markerAnnotationView.displayPriority = MKFeatureDisplayPriorityRequired;
431
452
  markerAnnotationView.layer.zPosition = markerView.zIndex;
432
453
  markerAnnotationView.zPriority = markerView.zIndex;
433
454
  markerAnnotationView.draggable = markerView.draggable;
455
+ [self applyCalloutView:markerView annotationView:markerAnnotationView];
434
456
  [self addCenterTapGesture:markerAnnotationView];
435
457
  markerAnnotation.annotationView = markerAnnotationView;
436
458
  return markerAnnotationView;
437
459
  }
438
460
 
439
- MKAnnotationView *annotationView =
440
- [[MKAnnotationView alloc] initWithAnnotation:annotation
441
- reuseIdentifier:nil];
461
+ LuggAnnotationView *annotationView =
462
+ [[LuggAnnotationView alloc] initWithAnnotation:annotation
463
+ reuseIdentifier:nil];
442
464
  annotationView.canShowCallout = YES;
443
465
  annotationView.displayPriority = MKFeatureDisplayPriorityRequired;
444
466
  annotationView.layer.zPosition = markerView.zIndex;
@@ -446,6 +468,8 @@
446
468
  annotationView.draggable = markerView.draggable;
447
469
  [self addCenterTapGesture:annotationView];
448
470
 
471
+ [self applyCalloutView:markerView annotationView:annotationView];
472
+
449
473
  if (!markerView.rasterize) {
450
474
  UIView *iconView = markerView.iconView;
451
475
  [iconView removeFromSuperview];
@@ -516,6 +540,104 @@
516
540
  CGPoint point = [_mapView convertCoordinate:markerView.coordinate
517
541
  toPointToView:_mapView];
518
542
  [markerView emitPressEventWithPoint:point];
543
+
544
+ LuggCalloutView *calloutView = markerView.calloutView;
545
+ if (calloutView && !calloutView.bubbled && calloutView.hasCustomContent) {
546
+ [self showNonBubbledCallout:markerView];
547
+ }
548
+ }
549
+ }
550
+
551
+ - (void)mapView:(MKMapView *)mapView
552
+ didDeselectAnnotationView:(MKAnnotationView *)view {
553
+ if (![view.annotation isKindOfClass:[AppleMarkerAnnotation class]])
554
+ return;
555
+
556
+ AppleMarkerAnnotation *annotation = (AppleMarkerAnnotation *)view.annotation;
557
+ LuggMarkerView *markerView = annotation.markerView;
558
+
559
+ if (markerView && _activeNonBubbledMarker == markerView &&
560
+ !_isReselectingAnnotation) {
561
+ _isReselectingAnnotation = YES;
562
+ [_mapView selectAnnotation:annotation animated:NO];
563
+ _isReselectingAnnotation = NO;
564
+ }
565
+ }
566
+
567
+ - (void)showNonBubbledCallout:(LuggMarkerView *)markerView {
568
+ [self dismissNonBubbledCallout];
569
+
570
+ LuggCalloutView *calloutView = markerView.calloutView;
571
+ UIView *contentView = calloutView.contentView;
572
+ [contentView removeFromSuperview];
573
+
574
+ contentView.userInteractionEnabled = YES;
575
+ calloutView.delegate = self;
576
+
577
+ AppleMarkerAnnotation *annotation =
578
+ (AppleMarkerAnnotation *)markerView.marker;
579
+ MKAnnotationView *annotationView = annotation.annotationView;
580
+ if (annotationView) {
581
+ annotationView.clipsToBounds = NO;
582
+ [annotationView addSubview:contentView];
583
+ } else {
584
+ [_wrapperView addSubview:contentView];
585
+ }
586
+
587
+ _activeNonBubbledMarker = markerView;
588
+ [self positionNonBubbledCallout];
589
+ }
590
+
591
+ - (void)calloutViewDidUpdate:(LuggCalloutView *)calloutView {
592
+ [self positionNonBubbledCallout];
593
+ }
594
+
595
+ - (void)positionNonBubbledCallout {
596
+ if (!_activeNonBubbledMarker)
597
+ return;
598
+
599
+ LuggCalloutView *calloutView = _activeNonBubbledMarker.calloutView;
600
+ UIView *contentView = calloutView.contentView;
601
+ CGSize contentSize = contentView.bounds.size;
602
+ if (contentSize.width <= 0 || contentSize.height <= 0)
603
+ return;
604
+
605
+ CGPoint anchor = calloutView.anchor;
606
+
607
+ AppleMarkerAnnotation *annotation =
608
+ (AppleMarkerAnnotation *)_activeNonBubbledMarker.marker;
609
+ MKAnnotationView *annotationView = annotation.annotationView;
610
+
611
+ if (annotationView && contentView.superview == annotationView) {
612
+ CGPoint center = CGPointMake(annotationView.bounds.size.width / 2.0 +
613
+ contentSize.width * (0.5 - anchor.x),
614
+ annotationView.bounds.size.height / 2.0 +
615
+ contentSize.height * (0.5 - anchor.y));
616
+ contentView.center = center;
617
+ } else {
618
+ CGPoint point =
619
+ [_mapView convertCoordinate:_activeNonBubbledMarker.coordinate
620
+ toPointToView:_wrapperView];
621
+ contentView.center =
622
+ CGPointMake(point.x + contentSize.width * (0.5 - anchor.x),
623
+ point.y + contentSize.height * (0.5 - anchor.y));
624
+ }
625
+ }
626
+
627
+ - (void)dismissNonBubbledCallout {
628
+ if (!_activeNonBubbledMarker)
629
+ return;
630
+
631
+ LuggMarkerView *markerView = _activeNonBubbledMarker;
632
+ _activeNonBubbledMarker = nil;
633
+
634
+ markerView.calloutView.delegate = nil;
635
+ [markerView.calloutView.contentView removeFromSuperview];
636
+
637
+ AppleMarkerAnnotation *annotation =
638
+ (AppleMarkerAnnotation *)markerView.marker;
639
+ if (annotation) {
640
+ [_mapView deselectAnnotation:annotation animated:NO];
519
641
  }
520
642
  }
521
643
 
@@ -574,6 +696,27 @@
574
696
  }
575
697
  }
576
698
 
699
+ - (void)applyCalloutView:(LuggMarkerView *)markerView
700
+ annotationView:(MKAnnotationView *)annotationView {
701
+ LuggCalloutView *calloutView = markerView.calloutView;
702
+ if (!calloutView) {
703
+ annotationView.detailCalloutAccessoryView = nil;
704
+ annotationView.rightCalloutAccessoryView = nil;
705
+ return;
706
+ }
707
+
708
+ annotationView.rightCalloutAccessoryView = nil;
709
+
710
+ if (!calloutView.bubbled) {
711
+ annotationView.canShowCallout = NO;
712
+ annotationView.detailCalloutAccessoryView = nil;
713
+ return;
714
+ }
715
+
716
+ annotationView.detailCalloutAccessoryView =
717
+ calloutView.hasCustomContent ? calloutView.contentView : nil;
718
+ }
719
+
577
720
  #pragma mark - MarkerViewDelegate
578
721
 
579
722
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
@@ -600,6 +743,7 @@
600
743
  annotationView.draggable = markerView.draggable;
601
744
  annotationView.layer.zPosition = markerView.zIndex;
602
745
  annotationView.zPriority = markerView.zIndex;
746
+ [self applyCalloutView:markerView annotationView:annotationView];
603
747
  }
604
748
 
605
749
  [self updateAnnotationViewFrame:annotation];
@@ -1,4 +1,5 @@
1
1
  #import "GoogleMapProvider.h"
2
+ #import "../LuggCalloutView.h"
2
3
  #import "../LuggMarkerView.h"
3
4
  #import "../LuggPolygonView.h"
4
5
  #import "../LuggPolylineView.h"
@@ -8,10 +9,12 @@
8
9
  static NSString *const kDemoMapId = @"DEMO_MAP_ID";
9
10
 
10
11
  @interface GoogleMapProvider () <
11
- LuggMarkerViewDelegate, LuggPolylineViewDelegate, LuggPolygonViewDelegate>
12
+ LuggMarkerViewDelegate, LuggCalloutViewDelegate, LuggPolylineViewDelegate,
13
+ LuggPolygonViewDelegate>
12
14
  @end
13
15
 
14
16
  @implementation GoogleMapProvider {
17
+ UIView *_wrapperView;
15
18
  GMSMapView *_mapView;
16
19
  BOOL _isMapReady;
17
20
  BOOL _isDragging;
@@ -23,6 +26,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
23
26
  NSMapTable<LuggPolylineView *, GMSPolylineAnimator *> *_polylineAnimators;
24
27
  NSMapTable<GMSPolygon *, LuggPolygonView *> *_polygonToViewMap;
25
28
  NSMapTable<GMSMarker *, LuggMarkerView *> *_markerToViewMap;
29
+ LuggMarkerView *_activeNonBubbledMarker;
26
30
 
27
31
  // Edge insets animation
28
32
  CADisplayLink *_edgeInsetsDisplayLink;
@@ -88,6 +92,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
88
92
  _mapView.paddingAdjustmentBehavior =
89
93
  kGMSMapViewPaddingAdjustmentBehaviorNever;
90
94
 
95
+ _wrapperView = wrapperView;
91
96
  [wrapperView addSubview:_mapView];
92
97
 
93
98
  [self applyTheme];
@@ -248,6 +253,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
248
253
  longitude:position.target.longitude
249
254
  zoom:position.zoom
250
255
  gesture:_isDragging];
256
+ [self positionNonBubbledCallout];
251
257
  }
252
258
 
253
259
  - (void)mapView:(GMSMapView *)mapView
@@ -267,6 +273,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
267
273
 
268
274
  - (void)mapView:(GMSMapView *)mapView
269
275
  didTapAtCoordinate:(CLLocationCoordinate2D)coordinate {
276
+ [self dismissNonBubbledCallout];
270
277
  CGPoint point = [mapView.projection pointForCoordinate:coordinate];
271
278
  [_delegate mapProviderDidPress:coordinate.latitude
272
279
  longitude:coordinate.longitude
@@ -296,10 +303,19 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
296
303
  }
297
304
 
298
305
  - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
306
+ [self dismissNonBubbledCallout];
307
+
299
308
  LuggMarkerView *markerView = [_markerToViewMap objectForKey:marker];
300
309
  if (markerView) {
301
310
  CGPoint point = [_mapView.projection pointForCoordinate:marker.position];
302
311
  [markerView emitPressEventWithPoint:point];
312
+
313
+ LuggCalloutView *calloutView = markerView.calloutView;
314
+ if (calloutView && !calloutView.bubbled && calloutView.hasCustomContent) {
315
+ [mapView animateToLocation:marker.position];
316
+ [self showNonBubbledCallout:markerView];
317
+ return YES;
318
+ }
303
319
  }
304
320
  return NO;
305
321
  }
@@ -332,6 +348,95 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
332
348
  }
333
349
  }
334
350
 
351
+ - (UIView *)rasterizedCalloutView:(LuggCalloutView *)calloutView {
352
+ UIView *contentView = calloutView.contentView;
353
+ [contentView layoutIfNeeded];
354
+
355
+ CGSize size = contentView.bounds.size;
356
+ if (size.width <= 0 || size.height <= 0)
357
+ return nil;
358
+
359
+ UIGraphicsImageRendererFormat *format =
360
+ [UIGraphicsImageRendererFormat defaultFormat];
361
+ format.scale = [UIScreen mainScreen].scale;
362
+ UIGraphicsImageRenderer *renderer =
363
+ [[UIGraphicsImageRenderer alloc] initWithSize:size format:format];
364
+ UIImage *image =
365
+ [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) {
366
+ [contentView.layer renderInContext:context.CGContext];
367
+ }];
368
+
369
+ return [[UIImageView alloc] initWithImage:image];
370
+ }
371
+
372
+ - (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker {
373
+ // Non-bubbled callouts are rendered as live views, not info windows
374
+ return nil;
375
+ }
376
+
377
+ - (UIView *)mapView:(GMSMapView *)mapView
378
+ markerInfoContents:(GMSMarker *)marker {
379
+ LuggMarkerView *markerView = [_markerToViewMap objectForKey:marker];
380
+ if (!markerView || !markerView.calloutView ||
381
+ !markerView.calloutView.hasCustomContent)
382
+ return nil;
383
+
384
+ if (markerView.calloutView.bubbled) {
385
+ return [self rasterizedCalloutView:markerView.calloutView];
386
+ }
387
+
388
+ return nil;
389
+ }
390
+
391
+ - (void)showNonBubbledCallout:(LuggMarkerView *)markerView {
392
+ [self dismissNonBubbledCallout];
393
+ _mapView.selectedMarker = nil;
394
+
395
+ LuggCalloutView *calloutView = markerView.calloutView;
396
+ UIView *contentView = calloutView.contentView;
397
+ [contentView removeFromSuperview];
398
+
399
+ contentView.userInteractionEnabled = YES;
400
+ calloutView.delegate = self;
401
+ [_wrapperView addSubview:contentView];
402
+
403
+ _activeNonBubbledMarker = markerView;
404
+ [self positionNonBubbledCallout];
405
+ }
406
+
407
+ - (void)calloutViewDidUpdate:(LuggCalloutView *)calloutView {
408
+ [self positionNonBubbledCallout];
409
+ }
410
+
411
+ - (void)dismissNonBubbledCallout {
412
+ if (!_activeNonBubbledMarker)
413
+ return;
414
+
415
+ LuggCalloutView *calloutView = _activeNonBubbledMarker.calloutView;
416
+ calloutView.delegate = nil;
417
+ [calloutView.contentView removeFromSuperview];
418
+ _activeNonBubbledMarker = nil;
419
+ }
420
+
421
+ - (void)positionNonBubbledCallout {
422
+ if (!_activeNonBubbledMarker)
423
+ return;
424
+
425
+ LuggCalloutView *calloutView = _activeNonBubbledMarker.calloutView;
426
+ UIView *contentView = calloutView.contentView;
427
+ CGSize contentSize = contentView.bounds.size;
428
+ if (contentSize.width <= 0 || contentSize.height <= 0)
429
+ return;
430
+
431
+ CGPoint anchor = calloutView.anchor;
432
+ CGPoint point = [_mapView.projection
433
+ pointForCoordinate:_activeNonBubbledMarker.coordinate];
434
+
435
+ contentView.center =
436
+ CGPointMake(point.x + contentSize.width * (0.5 - anchor.x),
437
+ point.y + contentSize.height * (0.5 - anchor.y));
438
+ }
439
+
335
440
  #pragma mark - MarkerViewDelegate
336
441
 
337
442
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
@@ -387,8 +492,10 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
387
492
 
388
493
  GMSAdvancedMarker *marker = (GMSAdvancedMarker *)markerView.marker;
389
494
  marker.position = markerView.coordinate;
390
- marker.title = markerView.title;
391
- marker.snippet = markerView.markerDescription;
495
+ marker.title = markerView.title.length > 0 ? markerView.title : nil;
496
+ marker.snippet = markerView.markerDescription.length > 0
497
+ ? markerView.markerDescription
498
+ : nil;
392
499
  marker.zIndex = (int)markerView.zIndex;
393
500
  marker.draggable = markerView.draggable;
394
501
  [self applyMarkerStyle:markerView marker:marker];
@@ -410,8 +517,10 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
410
517
 
411
518
  GMSAdvancedMarker *marker = [[GMSAdvancedMarker alloc] init];
412
519
  marker.position = markerView.coordinate;
413
- marker.title = markerView.title;
414
- marker.snippet = markerView.markerDescription;
520
+ marker.title = markerView.title.length > 0 ? markerView.title : nil;
521
+ marker.snippet = markerView.markerDescription.length > 0
522
+ ? markerView.markerDescription
523
+ : nil;
415
524
  marker.zIndex = (int)markerView.zIndex;
416
525
  marker.draggable = markerView.draggable;
417
526
 
@@ -0,0 +1,12 @@
1
+ #import <MapKit/MapKit.h>
2
+
3
+ @interface LuggAnnotationView : MKAnnotationView
4
+ @end
5
+
6
+ @interface LuggMarkerAnnotationView : MKMarkerAnnotationView
7
+ @end
8
+
9
+ // Shared hit-test helpers for annotation views with subviews outside bounds
10
+ BOOL LuggAnnotationPointInside(UIView *self, CGPoint point, UIEvent *event);
11
+ UIView *_Nullable LuggAnnotationHitTest(UIView *self, CGPoint point,
12
+ UIEvent *event);