react-native-maps 1.21.0-alpha.4 → 1.21.0-alpha.40

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 (69) hide show
  1. package/android/src/main/java/com/rnmaps/fabric/NativeAirMapsModule.java +51 -0
  2. package/android/src/main/java/com/rnmaps/maps/MapAirModulePackage.java +46 -0
  3. package/ios/AirGoogleMaps/AIRGoogleMap.h +8 -2
  4. package/ios/AirGoogleMaps/AIRGoogleMap.m +179 -11
  5. package/ios/AirGoogleMaps/AIRGoogleMapCircle.h +0 -1
  6. package/ios/AirGoogleMaps/AIRGoogleMapCircle.m +23 -11
  7. package/ios/AirGoogleMaps/AIRGoogleMapCoordinate.h +13 -0
  8. package/ios/AirGoogleMaps/AIRGoogleMapCoordinate.m +10 -0
  9. package/ios/AirGoogleMaps/AIRGoogleMapManager.h +7 -0
  10. package/ios/AirGoogleMaps/AIRGoogleMapManager.m +30 -119
  11. package/ios/AirGoogleMaps/AIRGoogleMapMarker.h +2 -0
  12. package/ios/AirGoogleMaps/AIRGoogleMapMarker.m +347 -243
  13. package/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m +0 -1
  14. package/ios/AirGoogleMaps/AIRGoogleMapOverlay.h +0 -1
  15. package/ios/AirGoogleMaps/AIRGoogleMapPolygon.h +5 -4
  16. package/ios/AirGoogleMaps/AIRGoogleMapPolygon.m +32 -2
  17. package/ios/AirGoogleMaps/AIRGoogleMapPolygonManager.m +3 -3
  18. package/ios/AirGoogleMaps/AIRGoogleMapPolyline.h +3 -2
  19. package/ios/AirGoogleMaps/AIRGoogleMapPolyline.m +2 -3
  20. package/ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m +2 -2
  21. package/ios/AirGoogleMaps/AIRGoogleMapWMSTile.h +1 -1
  22. package/ios/AirGoogleMaps/AIRGoogleMapWMSTile.m +2 -2
  23. package/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.h +3 -0
  24. package/ios/AirGoogleMaps/RCTConvert+GMSMapViewType.m +18 -0
  25. package/ios/AirGoogleMaps/RNMapsGoogleMapView.h +26 -0
  26. package/ios/AirGoogleMaps/RNMapsGoogleMapView.mm +599 -0
  27. package/ios/AirGoogleMaps/RNMapsGoogleMapViewManager.mm +25 -0
  28. package/ios/AirGoogleMaps/RNMapsGooglePolygonView.h +26 -0
  29. package/ios/AirGoogleMaps/RNMapsGooglePolygonView.mm +217 -0
  30. package/ios/AirGoogleMaps/RNMapsGooglePolygonViewManager.mm +25 -0
  31. package/ios/AirMaps/AIRMap.h +8 -5
  32. package/ios/AirMaps/AIRMap.m +284 -141
  33. package/ios/AirMaps/AIRMapManager.m +12 -21
  34. package/ios/AirMaps/AIRMapMarker.h +1 -0
  35. package/ios/AirMaps/AIRMapMarker.m +165 -103
  36. package/ios/AirMaps/Callout/SMCalloutView.m +1 -1
  37. package/ios/AirMaps/RCTConvert+AirMap.h +4 -0
  38. package/ios/AirMaps/RCTConvert+AirMap.m +2 -0
  39. package/ios/AirMaps/RNMapsAirModule.h +3 -0
  40. package/ios/AirMaps/RNMapsAirModule.mm +42 -172
  41. package/ios/AirMaps/RNMapsAirModuleDelegate.h +24 -0
  42. package/ios/AirMaps/RNMapsHostVewDelegate.h +17 -0
  43. package/ios/AirMaps/RNMapsMapView.h +6 -3
  44. package/ios/AirMaps/RNMapsMapView.mm +341 -334
  45. package/ios/AirMaps/RNMapsMarkerView.h +24 -0
  46. package/ios/AirMaps/RNMapsMarkerView.mm +422 -0
  47. package/ios/AirMaps/RNMapsMarkerViewManager.mm +21 -0
  48. package/ios/AirMaps/UIView +AirMap.m +20 -0
  49. package/ios/AirMaps/UIView+AirMap.h +11 -0
  50. package/lib/MapMarker.d.ts +15 -2
  51. package/lib/MapMarker.js +53 -9
  52. package/lib/MapMarkerNativeComponent.js +0 -2
  53. package/lib/MapOverlay.d.ts +1 -1
  54. package/lib/MapView.d.ts +5 -22
  55. package/lib/MapView.js +34 -21
  56. package/lib/{FabricMapView.d.ts → createFabricMap.d.ts} +7 -6
  57. package/lib/createFabricMap.js +175 -0
  58. package/lib/decorateMapComponent.js +18 -0
  59. package/lib/specs/NativeAirMapsModule.d.ts +2 -2
  60. package/lib/specs/NativeComponentGoogleMapView.d.ts +713 -0
  61. package/lib/specs/NativeComponentGoogleMapView.js +21 -0
  62. package/lib/specs/NativeComponentGooglePolygon.d.ts +75 -0
  63. package/lib/specs/NativeComponentGooglePolygon.js +9 -0
  64. package/lib/specs/NativeComponentMapView.d.ts +21 -45
  65. package/lib/specs/NativeComponentMarker.d.ts +258 -0
  66. package/lib/specs/NativeComponentMarker.js +21 -0
  67. package/package.json +7 -7
  68. package/react-native-maps.podspec +1 -1
  69. package/lib/FabricMapView.js +0 -175
@@ -20,6 +20,7 @@
20
20
  #import "AIRMapWMSTile.h"
21
21
  #import "AIRMapLocalTile.h"
22
22
  #import "AIRMapOverlay.h"
23
+ #import "AIRMapSnapshot.h"
23
24
 
24
25
  const NSTimeInterval AIRMapRegionChangeObserveInterval = 0.1;
25
26
  const CGFloat AIRMapZoomBoundBuffer = 0.01;
@@ -38,8 +39,6 @@ const NSInteger AIRMapMaxZoomLevel = 20;
38
39
  @property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
39
40
  @property (nonatomic, assign) NSNumber *shouldZoomEnabled;
40
41
  @property (nonatomic, assign) NSNumber *shouldScrollEnabled;
41
- @property (nonatomic, strong) MKCompassButton *defaultCompassButton;
42
- @property (nonatomic, strong) MKCompassButton *overlayCompassButton;
43
42
 
44
43
  - (void)updateScrollEnabled;
45
44
  - (void)updateZoomEnabled;
@@ -51,7 +50,9 @@ const NSInteger AIRMapMaxZoomLevel = 20;
51
50
  UIView *_legalLabel;
52
51
  BOOL _initialRegionSet;
53
52
  BOOL _initialCameraSet;
54
-
53
+ BOOL _initialized;
54
+ BOOL _loadingStarted;
55
+
55
56
  // Array to manually track RN subviews
56
57
  //
57
58
  // AIRMap implicitly creates subviews that aren't regular RN children
@@ -69,7 +70,7 @@ const NSInteger AIRMapMaxZoomLevel = 20;
69
70
  if ((self = [super init])) {
70
71
  _hasStartedRendering = NO;
71
72
  _reactSubviews = [NSMutableArray new];
72
-
73
+
73
74
  // Find Apple link label
74
75
  for (UIView *subview in self.subviews) {
75
76
  if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) {
@@ -79,16 +80,18 @@ const NSInteger AIRMapMaxZoomLevel = 20;
79
80
  break;
80
81
  }
81
82
  }
82
-
83
+
83
84
  // 3rd-party callout view for MapKit that has more options than the built-in. It's painstakingly built to
84
85
  // be identical to the built-in callout view (which has a private API)
85
86
  self.calloutView = [SMCalloutView platformCalloutView];
86
87
  self.calloutView.delegate = self;
87
-
88
- self.minZoomLevel = 0;
89
- self.maxZoomLevel = AIRMapMaxZoomLevel;
88
+
89
+ self.minZoom = 0;
90
+ self.maxZoom = AIRMapMaxZoomLevel;
90
91
  self.compassOffset = CGPointMake(0, 0);
91
92
  self.legacyZoomConstraintsEnabled = YES;
93
+ _initialized = YES;
94
+ [self listenToMemoryWarnings];
92
95
  }
93
96
  return self;
94
97
  }
@@ -199,6 +202,20 @@ const NSInteger AIRMapMaxZoomLevel = 20;
199
202
  }
200
203
  return marker;
201
204
  }
205
+ // Create Polyline with coordinates
206
+ -(void) fitToCoordinates:(NSArray<AIRMapCoordinate*>*) coordinates edgePadding:(UIEdgeInsets) edgeInsets animated:(Boolean) animated {
207
+ CLLocationCoordinate2D coords[coordinates.count];
208
+ for(int i = 0; i < coordinates.count; i++)
209
+ {
210
+ coords[i] = coordinates[i].coordinate;
211
+ }
212
+ MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordinates.count];
213
+
214
+ // Set Map viewport
215
+
216
+ [self setVisibleMapRect:[polyline boundingMapRect] edgePadding:edgeInsets animated:animated];
217
+ }
218
+
202
219
 
203
220
  - (CGRect) frameForMarker:(AIRMapMarker*) mrkAnn {
204
221
  MKAnnotationView* mrkView = [self viewForAnnotation: mrkAnn];
@@ -206,29 +223,6 @@ const NSInteger AIRMapMaxZoomLevel = 20;
206
223
  return mrkFrame;
207
224
  }
208
225
 
209
- - (NSDictionary*) getMarkersFramesWithOnlyVisible:(BOOL)onlyVisible {
210
- NSMutableDictionary* markersFrames = [NSMutableDictionary new];
211
- for (AIRMapMarker* mrkAnn in self.markers) {
212
- CGRect frame = [self frameForMarker:mrkAnn];
213
- CGPoint point = [self convertCoordinate:mrkAnn.coordinate toPointToView:self];
214
- NSDictionary* frameDict = @{
215
- @"x": @(frame.origin.x),
216
- @"y": @(frame.origin.y),
217
- @"width": @(frame.size.width),
218
- @"height": @(frame.size.height)
219
- };
220
- NSDictionary* pointDict = @{
221
- @"x": @(point.x),
222
- @"y": @(point.y)
223
- };
224
- NSString* k = mrkAnn.identifier;
225
- BOOL isVisible = CGRectIntersectsRect(self.bounds, frame);
226
- if (k != nil && (!onlyVisible || isVisible)) {
227
- [markersFrames setObject:@{ @"frame": frameDict, @"point": pointDict } forKey:k];
228
- }
229
- }
230
- return markersFrames;
231
- }
232
226
 
233
227
  - (AIRMapMarker*) markerAtPoint:(CGPoint)point {
234
228
  AIRMapMarker* mrk = nil;
@@ -257,16 +251,16 @@ const NSInteger AIRMapMaxZoomLevel = 20;
257
251
  // Allow touches to be sent to our calloutview.
258
252
  // See this for some discussion of why we need to override this: https://github.com/nfarina/calloutview/pull/9
259
253
  - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
260
-
254
+
261
255
  CGPoint touchPoint = [self.calloutView convertPoint:point fromView:self];
262
256
  UIView *touchedView = [self.calloutView hitTest:touchPoint withEvent:event];
263
-
257
+
264
258
  if (touchedView) {
265
259
  UIWindow* win = [[[UIApplication sharedApplication] windows] firstObject];
266
260
  AIRMapCalloutSubview* calloutSubview = nil;
267
261
  AIRMapCallout* callout = nil;
268
262
  AIRMapMarker* marker = nil;
269
-
263
+
270
264
  UIView* tmp = touchedView;
271
265
  while (tmp && tmp != win && tmp != self.calloutView) {
272
266
  if ([tmp respondsToSelector:@selector(onPress)]) {
@@ -278,7 +272,7 @@ const NSInteger AIRMapMaxZoomLevel = 20;
278
272
  }
279
273
  tmp = tmp.superview;
280
274
  }
281
-
275
+
282
276
  if (callout) {
283
277
  marker = [self markerForCallout:callout];
284
278
  if (marker) {
@@ -288,49 +282,49 @@ const NSInteger AIRMapMaxZoomLevel = 20;
288
282
  }
289
283
  }
290
284
  }
291
-
285
+
292
286
  return calloutSubview ? calloutSubview : touchedView;
293
287
  }
294
-
288
+
295
289
  return [super hitTest:point withEvent:event];
296
290
  }
297
291
 
298
292
  #pragma mark SMCalloutViewDelegate
299
293
 
300
294
  - (NSTimeInterval)calloutView:(SMCalloutView *)calloutView delayForRepositionWithSize:(CGSize)offset {
301
-
295
+
302
296
  // When the callout is being asked to present in a way where it or its target will be partially offscreen, it asks us
303
297
  // if we'd like to reposition our surface first so the callout is completely visible. Here we scroll the map into view,
304
298
  // but it takes some math because we have to deal in lon/lat instead of the given offset in pixels.
305
-
299
+
306
300
  CLLocationCoordinate2D coordinate = self.region.center;
307
-
301
+
308
302
  // where's the center coordinate in terms of our view?
309
303
  CGPoint center = [self convertCoordinate:coordinate toPointToView:self];
310
-
304
+
311
305
  // move it by the requested offset
312
306
  center.x -= offset.width;
313
307
  center.y -= offset.height;
314
-
308
+
315
309
  // and translate it back into map coordinates
316
310
  coordinate = [self convertPoint:center toCoordinateFromView:self];
317
-
311
+
318
312
  // move the map!
319
313
  [self setCenterCoordinate:coordinate animated:YES];
320
-
314
+
321
315
  // tell the callout to wait for a while while we scroll (we assume the scroll delay for MKMapView matches UIScrollView)
322
316
  return kSMCalloutViewRepositionDelayForUIScrollView;
323
317
  }
324
318
 
325
- #pragma mark Accessors
319
+ #pragma mark RNMapsAirModuleDelegate.h
326
320
 
327
321
  - (NSArray *)getMapBoundaries
328
322
  {
329
323
  MKMapRect mapRect = self.visibleMapRect;
330
-
324
+
331
325
  CLLocationCoordinate2D northEast = MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMaxX(mapRect), mapRect.origin.y));
332
326
  CLLocationCoordinate2D southWest = MKCoordinateForMapPoint(MKMapPointMake(mapRect.origin.x, MKMapRectGetMaxY(mapRect)));
333
-
327
+
334
328
  return @[
335
329
  @[
336
330
  [NSNumber numberWithDouble:northEast.longitude],
@@ -342,6 +336,174 @@ const NSInteger AIRMapMaxZoomLevel = 20;
342
336
  ]
343
337
  ];
344
338
  }
339
+ - (NSDictionary *) getPointForCoordinates:(CLLocationCoordinate2D) location
340
+ {
341
+ CGPoint touchPoint = [self convertCoordinate:location toPointToView:self];
342
+
343
+ return @{
344
+ @"x": @(touchPoint.x),
345
+ @"y": @(touchPoint.y),
346
+ };
347
+ }
348
+ - (NSDictionary *) getCoordinatesForPoint:(CGPoint)point
349
+ {
350
+ CLLocationCoordinate2D coordinate = [self convertPoint:point toCoordinateFromView:self];
351
+ return @{
352
+ @"latitude": @(coordinate.latitude),
353
+ @"longitude": @(coordinate.longitude),
354
+ };
355
+
356
+ }
357
+
358
+ - (NSDictionary*) getMarkersFramesWithOnlyVisible:(BOOL)onlyVisible {
359
+ NSMutableDictionary* markersFrames = [NSMutableDictionary new];
360
+ for (AIRMapMarker* mrkAnn in self.markers) {
361
+ CGRect frame = [self frameForMarker:mrkAnn];
362
+ CGPoint point = [self convertCoordinate:mrkAnn.coordinate toPointToView:self];
363
+ NSDictionary* frameDict = @{
364
+ @"x": @(frame.origin.x),
365
+ @"y": @(frame.origin.y),
366
+ @"width": @(frame.size.width),
367
+ @"height": @(frame.size.height)
368
+ };
369
+ NSDictionary* pointDict = @{
370
+ @"x": @(point.x),
371
+ @"y": @(point.y)
372
+ };
373
+ NSString* k = mrkAnn.identifier;
374
+ BOOL isVisible = CGRectIntersectsRect(self.bounds, frame);
375
+ if (k != nil && (!onlyVisible || isVisible)) {
376
+ [markersFrames setObject:@{ @"frame": frameDict, @"point": pointDict } forKey:k];
377
+ }
378
+ }
379
+ return markersFrames;
380
+ }
381
+
382
+ - (NSDictionary *) getCameraDic {
383
+ MKMapCamera *camera = [self camera];
384
+ return @{
385
+ @"center": @{
386
+ @"latitude": @(camera.centerCoordinate.latitude),
387
+ @"longitude": @(camera.centerCoordinate.longitude),
388
+ },
389
+ @"pitch": @(camera.pitch),
390
+ @"heading": @(camera.heading),
391
+ @"altitude": @(camera.altitude),
392
+ };
393
+ }
394
+ - (void)takeSnapshotWithConfig:(NSDictionary *)config
395
+ success:(RCTPromiseResolveBlock)success error:(RCTPromiseRejectBlock)error
396
+ {
397
+
398
+ MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
399
+
400
+ options.mapType = self.mapType;
401
+ NSNumber *width = config[@"width"];
402
+ NSNumber *height = config[@"height"];
403
+ NSNumber *quality = config[@"quality"];
404
+ NSString*format =config[@"format"];
405
+ NSString*result =config[@"result"];
406
+ if (config[@"region"]){
407
+ MKCoordinateRegion region = [RCTConvert MKCoordinateRegion:config[@"region"]];
408
+ options.region = region;
409
+ } else
410
+ {
411
+ options.region = self.region;
412
+ }
413
+ options.size = CGSizeMake(
414
+ ([width floatValue] == 0) ? self.bounds.size.width : [width floatValue],
415
+ ([height floatValue] == 0) ? self.bounds.size.height : [height floatValue]
416
+ );
417
+
418
+ options.scale = [[UIScreen mainScreen] scale];
419
+
420
+
421
+ MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
422
+
423
+ [self takeMapSnapshot:snapshotter
424
+ format:format
425
+ quality:[quality floatValue]
426
+ result:result
427
+ success:success error:error];
428
+ }
429
+
430
+ #pragma mark Take Snapshot
431
+ - (void)takeMapSnapshot:(MKMapSnapshotter *) snapshotter
432
+ format:(NSString *)format
433
+ quality:(CGFloat) quality
434
+ result:(NSString *)result
435
+ success:(RCTPromiseResolveBlock) success
436
+ error:(RCTPromiseRejectBlock) errorCallback {
437
+ NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
438
+ NSString *pathComponent = [NSString stringWithFormat:@"Documents/snapshot-%.20lf.%@", timeStamp, format];
439
+ NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent: pathComponent];
440
+
441
+ [snapshotter startWithQueue:dispatch_get_main_queue()
442
+ completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
443
+ if (error) {
444
+ errorCallback(@"snapshotterError", @"failed", error);
445
+ return;
446
+ }
447
+ MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:nil];
448
+
449
+ UIImage *image = snapshot.image;
450
+ UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
451
+ {
452
+ [image drawAtPoint:CGPointMake(0.0f, 0.0f)];
453
+
454
+ CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
455
+
456
+ for (id <AIRMapSnapshot> overlay in self.overlays) {
457
+ if ([overlay respondsToSelector:@selector(drawToSnapshot:context:)]) {
458
+ [overlay drawToSnapshot:snapshot context:UIGraphicsGetCurrentContext()];
459
+ }
460
+ }
461
+
462
+
463
+ for (id <MKAnnotation> annotation in self.annotations) {
464
+ CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
465
+
466
+ MKAnnotationView* anView = [self viewForAnnotation: annotation];
467
+
468
+ if (anView){
469
+ pin = anView;
470
+ }
471
+
472
+ if (CGRectContainsPoint(rect, point)) {
473
+ point.x = point.x + pin.centerOffset.x - (pin.bounds.size.width / 2.0f);
474
+ point.y = point.y + pin.centerOffset.y - (pin.bounds.size.height / 2.0f);
475
+ if (pin.image) {
476
+ [pin.image drawAtPoint:point];
477
+ } else {
478
+ CGRect pinRect = CGRectMake(point.x, point.y, pin.bounds.size.width, pin.bounds.size.height);
479
+ [pin drawViewHierarchyInRect:pinRect afterScreenUpdates:NO];
480
+ }
481
+ }
482
+ }
483
+
484
+ UIImage *compositeImage = UIGraphicsGetImageFromCurrentImageContext();
485
+
486
+ NSData *data;
487
+ if ([format isEqualToString:@"png"]) {
488
+ data = UIImagePNGRepresentation(compositeImage);
489
+ }
490
+ else if([format isEqualToString:@"jpg"]) {
491
+ data = UIImageJPEGRepresentation(compositeImage, quality);
492
+ }
493
+
494
+ if ([result isEqualToString:@"file"]) {
495
+ [data writeToFile:filePath atomically:YES];
496
+ success(filePath);
497
+ }
498
+ else if ([result isEqualToString:@"base64"]) {
499
+ success([data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]);
500
+ }
501
+ }
502
+ UIGraphicsEndImageContext();
503
+ }];
504
+ }
505
+
506
+
345
507
 
346
508
  - (void)setShowsUserLocation:(BOOL)showsUserLocation
347
509
  {
@@ -380,13 +542,14 @@ const NSInteger AIRMapMaxZoomLevel = 20;
380
542
  _userLocationCalloutEnabled = calloutEnabled;
381
543
  }
382
544
 
383
- - (void)setHandlePanDrag:(BOOL)handleMapDrag {
545
+ - (void)setHandlePanDrag:(BOOL)handlePanDrag {
384
546
  for (UIGestureRecognizer *recognizer in [self gestureRecognizers]) {
385
547
  if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
386
- recognizer.enabled = handleMapDrag;
548
+ recognizer.enabled = handlePanDrag;
387
549
  break;
388
550
  }
389
551
  }
552
+ _handlePanDrag = handlePanDrag;
390
553
  }
391
554
 
392
555
  - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated
@@ -395,7 +558,7 @@ const NSInteger AIRMapMaxZoomLevel = 20;
395
558
  if (!CLLocationCoordinate2DIsValid(region.center)) {
396
559
  return;
397
560
  }
398
-
561
+
399
562
  // If new span values are nil, use old values instead
400
563
  if (!region.span.latitudeDelta) {
401
564
  region.span.latitudeDelta = self.region.span.latitudeDelta;
@@ -403,13 +566,27 @@ const NSInteger AIRMapMaxZoomLevel = 20;
403
566
  if (!region.span.longitudeDelta) {
404
567
  region.span.longitudeDelta = self.region.span.longitudeDelta;
405
568
  }
406
-
569
+
407
570
  // Animate/move to new position
408
571
  [super setRegion:region animated:animated];
409
572
  }
573
+ - (void) setRegion:(MKCoordinateRegion)region
574
+ {
575
+ if (!CLLocationCoordinate2DIsValid(region.center)) {
576
+ return;
577
+ }
578
+ if (!CLLocationCoordinate2DIsValid(_initialRegion.center)) {
579
+ [self setInitialRegion:region];
580
+ }
581
+ [self setRegion:region animated:NO];
582
+ }
410
583
 
411
584
  - (void)setInitialRegion:(MKCoordinateRegion)initialRegion {
412
- if (!_initialRegionSet) {
585
+ if (!CLLocationCoordinate2DIsValid(initialRegion.center)) {
586
+ return;
587
+ }
588
+ _initialRegion = initialRegion;
589
+ if (!_initialRegionSet && _loadingStarted) {
413
590
  _initialRegionSet = YES;
414
591
  [self setRegion:initialRegion animated:NO];
415
592
  }
@@ -422,9 +599,12 @@ const NSInteger AIRMapMaxZoomLevel = 20;
422
599
 
423
600
 
424
601
  - (void)setInitialCamera:(MKMapCamera*)initialCamera {
425
- if (!_initialCameraSet) {
426
- _initialCameraSet = YES;
427
- [self setCamera:initialCamera animated:NO];
602
+ _initialCamera = initialCamera;
603
+ if (initialCamera){
604
+ if (!_initialCameraSet && _loadingStarted) {
605
+ _initialCameraSet = YES;
606
+ [self setCamera:initialCamera animated:NO];
607
+ }
428
608
  }
429
609
  }
430
610
 
@@ -471,25 +651,25 @@ const NSInteger AIRMapMaxZoomLevel = 20;
471
651
 
472
652
  - (void)setCameraZoomRange:(NSDictionary *)cameraZoomRange {
473
653
  if (@available(iOS 13.0, *)) {
474
-
654
+
475
655
  if (cameraZoomRange == nil) {
476
656
  cameraZoomRange = @{};
477
657
  }
478
-
658
+
479
659
  NSNumber *minValue = cameraZoomRange[@"minCenterCoordinateDistance"];
480
660
  NSNumber *maxValue = cameraZoomRange[@"maxCenterCoordinateDistance"];
481
-
661
+
482
662
  if (minValue == nil && maxValue == nil) {
483
663
  self.legacyZoomConstraintsEnabled = YES;
484
-
664
+
485
665
  MKMapCameraZoomRange *defaultZoomRange = [[MKMapCameraZoomRange alloc] initWithMinCenterCoordinateDistance:MKMapCameraZoomDefault maxCenterCoordinateDistance:MKMapCameraZoomDefault];
486
666
  [super setCameraZoomRange:defaultZoomRange animated:NO];
487
-
667
+
488
668
  return;
489
669
  }
490
-
670
+
491
671
  MKMapCameraZoomRange *zoomRange = nil;
492
-
672
+
493
673
  if (minValue != nil && maxValue != nil) {
494
674
  zoomRange = [[MKMapCameraZoomRange alloc] initWithMinCenterCoordinateDistance:[minValue doubleValue] maxCenterCoordinateDistance:[maxValue doubleValue]];
495
675
  } else if (minValue != nil) {
@@ -497,61 +677,14 @@ const NSInteger AIRMapMaxZoomLevel = 20;
497
677
  } else if (maxValue != nil) {
498
678
  zoomRange = [[MKMapCameraZoomRange alloc] initWithMaxCenterCoordinateDistance:[maxValue doubleValue]];
499
679
  }
500
-
680
+
501
681
  BOOL animated = [cameraZoomRange[@"animated"] boolValue];
502
-
682
+
503
683
  self.legacyZoomConstraintsEnabled = NO;
504
684
  [super setCameraZoomRange:zoomRange animated:animated];
505
685
  }
506
686
  }
507
687
 
508
- // Include properties of MKMapView which are only available on iOS 9+
509
- // and check if their selector is available before calling super method.
510
-
511
- - (void)setShowsCompass:(BOOL)showsCompass {
512
- if (self.overlayCompassButton != nil) {
513
- self.overlayCompassButton.compassVisibility = showsCompass ? MKFeatureVisibilityAdaptive : MKFeatureVisibilityHidden;
514
- }
515
- if ([MKMapView instancesRespondToSelector:@selector(setShowsCompass:)]) {
516
- [super setShowsCompass:showsCompass];
517
- }
518
- }
519
-
520
- - (BOOL)showsCompass {
521
- if ([MKMapView instancesRespondToSelector:@selector(showsCompass)]) {
522
- return [super showsCompass];
523
- } else {
524
- return NO;
525
- }
526
- }
527
-
528
- - (void)setShowsScale:(BOOL)showsScale {
529
- if ([MKMapView instancesRespondToSelector:@selector(setShowsScale:)]) {
530
- [super setShowsScale:showsScale];
531
- }
532
- }
533
-
534
- - (BOOL)showsScale {
535
- if ([MKMapView instancesRespondToSelector:@selector(showsScale)]) {
536
- return [super showsScale];
537
- } else {
538
- return NO;
539
- }
540
- }
541
-
542
- - (void)setShowsTraffic:(BOOL)showsTraffic {
543
- if ([MKMapView instancesRespondToSelector:@selector(setShowsTraffic:)]) {
544
- [super setShowsTraffic:showsTraffic];
545
- }
546
- }
547
-
548
- - (BOOL)showsTraffic {
549
- if ([MKMapView instancesRespondToSelector:@selector(showsTraffic)]) {
550
- return [super showsTraffic];
551
- } else {
552
- return NO;
553
- }
554
- }
555
688
 
556
689
  - (void)setScrollEnabled:(BOOL)scrollEnabled {
557
690
  self.shouldScrollEnabled = [NSNumber numberWithBool:scrollEnabled];
@@ -597,7 +730,7 @@ const NSInteger AIRMapMaxZoomLevel = 20;
597
730
  else {
598
731
  self.cacheImageView.image = nil;
599
732
  self.cacheImageView.hidden = YES;
600
-
733
+
601
734
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
602
735
  self.cacheImageView.image = nil;
603
736
  self.cacheImageView.hidden = YES;
@@ -605,18 +738,47 @@ const NSInteger AIRMapMaxZoomLevel = 20;
605
738
  [self.layer renderInContext:UIGraphicsGetCurrentContext()];
606
739
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
607
740
  UIGraphicsEndImageContext();
608
-
741
+
609
742
  self.cacheImageView.image = image;
610
743
  self.cacheImageView.hidden = NO;
611
744
  });
612
745
  }
613
-
746
+
614
747
  [self updateScrollEnabled];
615
748
  [self updateZoomEnabled];
616
749
  [self updateLegalLabelInsets];
617
750
  }
618
751
  }
619
752
 
753
+ - (void) listenToMemoryWarnings
754
+ {
755
+ __weak typeof(self) weakSelf = self;
756
+ static dispatch_once_t onceToken;
757
+ static dispatch_source_t source;
758
+ dispatch_once(&onceToken, ^{
759
+ source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_WARN|DISPATCH_MEMORYPRESSURE_CRITICAL, dispatch_get_main_queue());
760
+ dispatch_source_set_event_handler(source, ^{
761
+ [weakSelf handleMemoryWarning];
762
+ });
763
+ dispatch_resume(source);
764
+ });
765
+ }
766
+ /*
767
+ hacky way to release all cache
768
+ this is our last chance to release cache before app crash
769
+ */
770
+ - (void) handleMemoryWarning
771
+ {
772
+ NSLog(@"handleMemoryWarning warning");
773
+ MKMapType type = self.mapType;
774
+ if (type == MKMapTypeSatellite){
775
+ self.mapType = MKMapTypeStandard;
776
+ } else {
777
+ self.mapType = MKMapTypeSatellite;
778
+ }
779
+ self.mapType = type;
780
+ }
781
+
620
782
  - (void)updateLegalLabelInsets {
621
783
  if (_legalLabel) {
622
784
  dispatch_async(dispatch_get_main_queue(), ^{
@@ -660,6 +822,10 @@ const NSInteger AIRMapMaxZoomLevel = 20;
660
822
  self.loadingView.hidden = YES;
661
823
  }
662
824
  }
825
+ _loadingStarted = YES;
826
+ // display initialRegion/Camera
827
+ [self setInitialRegion:_initialRegion];
828
+ [self setInitialCamera:_initialCamera];
663
829
  }
664
830
 
665
831
  - (void)finishLoading {
@@ -706,50 +872,27 @@ const NSInteger AIRMapMaxZoomLevel = 20;
706
872
  - (void)layoutSubviews {
707
873
  [super layoutSubviews];
708
874
  [self cacheViewIfNeeded];
709
-
710
- if (self.overlayCompassButton == nil) {
711
- for (UIView *subview in self.subviews) {
712
- if (![NSStringFromClass(subview.class) isEqualToString:@"MKPassThroughStackView"]) continue;
713
- self.overlayCompassButton = [MKCompassButton compassButtonWithMapView:self];
714
- [subview addSubview:self.overlayCompassButton];
715
- self.overlayCompassButton.frame = CGRectMake(self.overlayCompassButton.frame.origin.x + _compassOffset.x, self.overlayCompassButton.frame.origin.y + _compassOffset.y, self.overlayCompassButton.frame.size.width, self.overlayCompassButton.frame.size.height);
716
- break;
717
- }
718
- }
719
-
720
- if (self.defaultCompassButton == nil) {
721
- for (UIView *subview in self.subviews) {
722
- if (![NSStringFromClass(subview.class) isEqualToString:@"MKPassThroughStackView"]) continue;
723
- for (UIView *subview2 in subview.subviews) {
724
- if (![NSStringFromClass(subview2.class) isEqualToString:@"MKCompassView"]) continue;
725
- self.defaultCompassButton = subview2;
726
- self.defaultCompassButton.hidden = YES;
727
- }
728
- break;
729
- }
730
- }
731
-
732
875
  }
733
876
 
734
877
  // based on https://medium.com/@dmytrobabych/getting-actual-rotation-and-zoom-level-for-mapkit-mkmapview-e7f03f430aa9
735
878
  - (CGFloat)getZoomLevel {
736
879
  CGFloat cameraAngle = self.camera.heading;
737
-
880
+
738
881
  if (cameraAngle > 270) {
739
882
  cameraAngle = 360 - cameraAngle;
740
883
  } else if (cameraAngle > 90) {
741
884
  cameraAngle = fabs(cameraAngle - 180);
742
885
  }
743
-
886
+
744
887
  CGFloat angleRad = M_PI * cameraAngle / 180; // map rotation in radians
745
888
  CGFloat width = self.frame.size.width;
746
889
  CGFloat height = self.frame.size.height;
747
890
  CGFloat heightOffset = 20; // the offset (status bar height) which is taken by MapKit into consideration to calculate visible area height
748
-
891
+
749
892
  // calculating Longitude span corresponding to normal (non-rotated) width
750
893
  CGFloat spanStraight = width * self.region.span.longitudeDelta / (width * cos(angleRad) + (height - heightOffset) * sin(angleRad));
751
894
  int normalizingFactor = 512;
752
-
895
+
753
896
  return log2(360 * ((width / normalizingFactor) / spanStraight));
754
897
  }
755
898