/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import "RCTMap.h" #import "RCTEventDispatcher.h" #import "RCTLog.h" #import "RCTUtils.h" const CLLocationDegrees RCTMapDefaultSpan = 0.005; const NSTimeInterval RCTMapRegionChangeObserveInterval = 0.1; const CGFloat RCTMapZoomBoundBuffer = 0.01; @implementation RCTMap { UIView *_legalLabel; CLLocationManager *_locationManager; } - (instancetype)init { if ((self = [super init])) { _hasStartedRendering = NO; // Find Apple link label for (UIView *subview in self.subviews) { if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) { // This check is super hacky, but the whole premise of moving around // Apple's internal subviews is super hacky _legalLabel = subview; break; } } } return self; } - (void)dealloc { [_regionChangeObserveTimer invalidate]; } - (void)reactSetFrame:(CGRect)frame { self.frame = frame; } - (void)layoutSubviews { [super layoutSubviews]; if (_legalLabel) { dispatch_async(dispatch_get_main_queue(), ^{ CGRect frame = _legalLabel.frame; if (_legalLabelInsets.left) { frame.origin.x = _legalLabelInsets.left; } else if (_legalLabelInsets.right) { frame.origin.x = self.frame.size.width - _legalLabelInsets.right - frame.size.width; } if (_legalLabelInsets.top) { frame.origin.y = _legalLabelInsets.top; } else if (_legalLabelInsets.bottom) { frame.origin.y = self.frame.size.height - _legalLabelInsets.bottom - frame.size.height; } _legalLabel.frame = frame; }); } } #pragma mark Accessors - (void)setShowsUserLocation:(BOOL)showsUserLocation { if (self.showsUserLocation != showsUserLocation) { if (showsUserLocation && !_locationManager) { _locationManager = [CLLocationManager new]; if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { [_locationManager requestWhenInUseAuthorization]; } } super.showsUserLocation = showsUserLocation; // If it needs to show user location, force map view centered // on user's current location on user location updates _followUserLocation = showsUserLocation; } } - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated { // If location is invalid, abort if (!CLLocationCoordinate2DIsValid(region.center)) { return; } // If new span values are nil, use old values instead if (!region.span.latitudeDelta) { region.span.latitudeDelta = self.region.span.latitudeDelta; } if (!region.span.longitudeDelta) { region.span.longitudeDelta = self.region.span.longitudeDelta; } // Animate to new position [super setRegion:region animated:animated]; } - (void)setAnnotations:(RCTPointAnnotationArray *)annotations { NSMutableArray *newAnnotationIds = [NSMutableArray new]; NSMutableArray *annotationsToDelete = [NSMutableArray new]; NSMutableArray *annotationsToAdd = [NSMutableArray new]; for (RCTPointAnnotation *annotation in annotations) { if (![annotation isKindOfClass:[RCTPointAnnotation class]]) { continue; } [newAnnotationIds addObject:annotation.identifier]; // If the current set does not contain the new annotation, mark it as add if (![self.annotationIds containsObject:annotation.identifier]) { [annotationsToAdd addObject:annotation]; } } for (RCTPointAnnotation *annotation in self.annotations) { if (![annotation isKindOfClass:[RCTPointAnnotation class]]) { continue; } // If the new set does not contain an existing annotation, mark it as delete if (![newAnnotationIds containsObject:annotation.identifier]) { [annotationsToDelete addObject:annotation]; } } if (annotationsToDelete.count) { [self removeAnnotations:annotationsToDelete]; } if (annotationsToAdd.count) { [self addAnnotations:annotationsToAdd]; } NSMutableArray *newIds = [NSMutableArray new]; for (RCTPointAnnotation *anno in self.annotations) { if ([anno isKindOfClass:[MKUserLocation class]]) { continue; } [newIds addObject:anno.identifier]; } self.annotationIds = newIds; } @end