summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Bounds <jesse@rebounds.net>2016-11-01 11:48:09 -0700
committerGitHub <noreply@github.com>2016-11-01 11:48:09 -0700
commit2901bc0f6825f426cbcfd2a9ed78648dc81ab665 (patch)
treed3a050b065d3184db70c5726b4b1c5568c239624
parent8ffd1f69252d28bf501d5464ac9e5f6ff8942c89 (diff)
downloadqtlocation-mapboxgl-2901bc0f6825f426cbcfd2a9ed78648dc81ab665.tar.gz
Optimize annotation view updates (#5987)
Use queryPointAnnotations to drive annotation view updates - Get sets of visible and offscreen annotations using the mbgl query mechanism and updates and enqueues as required - Query viewport adjusted if tilted (avoid apparent issue with queryPointAnnotations when the query box is larger than the actual viewport) - Add a small debugging display in iOS app to see annotations going in and out of the reuse queue This also works around a performance issue that made getting an annotation context expensive by implementing a map of annotations to tags. It works around another issue with the underlying mbgl query so that even if it (rarely) returns an incorrect result, the correct visual effect still occurs and the reuse queue is added to and drained as expected. Finally, this refactors MGLMapView viewForAnnotation: to use the maps to access the requested annotation context and view. This avoids a more expensive lookup done previously. Along for the ride: sync up the ios and macos names (and types) for MGLAnnotationTagContextMap
-rw-r--r--platform/ios/CHANGELOG.md1
-rw-r--r--platform/ios/app/MBXViewController.m38
-rw-r--r--platform/ios/app/Main.storyboard62
-rw-r--r--platform/ios/src/MGLMapView.mm111
-rw-r--r--platform/macos/src/MGLMapView.mm4
5 files changed, 161 insertions, 55 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 13ee40c2b5..420714b88d 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -46,6 +46,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Annotations
* MGLPolyline annotations and the exterior coordinates of MGLPolygon annotations are now able to be mutated, part or all, and changes are displayed immediately. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565))
+* Fixed an issue causing offscreen annotation views to be updated even when they were in the reuse queue. ([#5987](https://github.com/mapbox/mapbox-gl-native/pull/5987))
* Fixed an issue preventing MGLAnnotationView from animating when its coordinate changes. ([#6215](https://github.com/mapbox/mapbox-gl-native/pull/6215))
* Fixed an issue causing the wrong annotation view to be selected when tapping an annotation view with a center offset applied. ([#5931](https://github.com/mapbox/mapbox-gl-native/pull/5931))
* Fixed an issue that assigned annotation views to polyline and polygon annotations. ([#5770](https://github.com/mapbox/mapbox-gl-native/pull/5770))
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index e8a78abff1..6eae0ef777 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -74,7 +74,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
};
typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- MBXSettingsMiscellaneousWorldTour = 0,
+ MBXSettingsMiscellaneousShowReuseQueueStats = 0,
+ MBXSettingsMiscellaneousWorldTour,
MBXSettingsMiscellaneousCustomUserDot,
MBXSettingsMiscellaneousPrintLogFile,
MBXSettingsMiscellaneousDeleteLogFile,
@@ -102,11 +103,21 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
UITableViewDataSource,
MGLMapViewDelegate>
+
@property (nonatomic) IBOutlet MGLMapView *mapView;
+@property (weak, nonatomic) IBOutlet UILabel *hudLabel;
@property (nonatomic) NSInteger styleIndex;
@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL customUserLocationAnnnotationEnabled;
@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
+@property (nonatomic) BOOL reuseQueueStatsEnabled;
+
+@end
+
+@interface MGLMapView (MBXViewController)
+
+@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
+@property (nonatomic) NSDictionary *annotationViewReuseQueueByIdentifier;
@end
@@ -140,6 +151,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self restoreState:nil];
self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
+ self.hudLabel.hidden = YES;
if ([MGLAccountManager accessToken].length)
{
@@ -323,6 +335,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
]];
break;
case MBXSettingsMiscellaneous:
+ [settingsTitles addObject:@"Show Reuse Queue Stats"];
+
[settingsTitles addObjectsFromArray:@[
@"Start World Tour",
[NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
@@ -335,6 +349,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Delete Telemetry Logfile",
]];
};
+
break;
default:
NSAssert(NO, @"All settings sections should be implemented");
@@ -495,6 +510,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsMiscellaneousDeleteLogFile:
[self deleteTelemetryLogFile];
break;
+ case MBXSettingsMiscellaneousShowReuseQueueStats:
+ {
+ self.reuseQueueStatsEnabled = YES;
+ self.hudLabel.hidden = NO;
+ break;
+ }
default:
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
break;
@@ -1506,9 +1527,24 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style
{
// Default Mapbox styles use {name_en} as their label language, which means
+ NSUInteger queuedAnnotations = 0;
// that a device with an English-language locale is already effectively
+ {
// using locale-based country labels.
+ }
_usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
}
+- (void)mapViewRegionIsChanging:(MGLMapView *)mapView
+{
+ if (self.reuseQueueStatsEnabled) {
+ NSUInteger queuedAnnotations = 0;
+ for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues)
+ {
+ queuedAnnotations += queue.count;
+ }
+ self.hudLabel.text = [NSString stringWithFormat:@"Visible: %ld Queued: %ld", (long)mapView.visibleAnnotations.count, (long)queuedAnnotations];
+ }
+}
+
@end
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 7d8b2c304f..5819f17edc 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -1,9 +1,11 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="PSe-Ot-7Ff">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
+ <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Map View Controller-->
@@ -15,24 +17,35 @@
<viewControllerLayoutGuide type="bottom" id="m8o-i7-QIy"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Z9X-fc-PUC">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kNe-zV-9ha" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
<connections>
<outlet property="delegate" destination="WaX-pd-UZQ" id="za0-3B-qR6"/>
<outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
</connections>
</view>
+ <label opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
+ <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="180" id="OL2-l5-I2f"/>
+ <constraint firstAttribute="height" constant="21" id="xHg-ye-wzT"/>
+ </constraints>
+ <fontDescription key="fontDescription" type="system" pointSize="8"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <nil key="highlightedColor"/>
+ </label>
</subviews>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kNe-zV-9ha" firstAttribute="leading" secondItem="Z9X-fc-PUC" secondAttribute="leading" id="53e-Tz-QxF"/>
<constraint firstItem="kNe-zV-9ha" firstAttribute="bottom" secondItem="m8o-i7-QIy" secondAttribute="top" id="Etp-BC-E1N"/>
<constraint firstAttribute="trailing" secondItem="kNe-zV-9ha" secondAttribute="trailing" id="MGr-8G-VEb"/>
+ <constraint firstItem="58y-pX-YyB" firstAttribute="trailing" secondItem="Z9X-fc-PUC" secondAttribute="trailingMargin" id="O3a-bR-boI"/>
+ <constraint firstItem="m8o-i7-QIy" firstAttribute="top" secondItem="58y-pX-YyB" secondAttribute="bottom" constant="20" id="cjh-ZS-Mv4"/>
<constraint firstItem="kNe-zV-9ha" firstAttribute="top" secondItem="Z9X-fc-PUC" secondAttribute="top" id="qMm-e9-jxH"/>
</constraints>
</view>
@@ -47,7 +60,7 @@
</connections>
</barButtonItem>
<button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
- <rect key="frame" x="180" y="7" width="240" height="30"/>
+ <rect key="frame" x="61" y="7" width="207" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<state key="normal" title="Streets"/>
@@ -75,6 +88,7 @@
</rightBarButtonItems>
</navigationItem>
<connections>
+ <outlet property="hudLabel" destination="58y-pX-YyB" id="MEh-ir-3IH"/>
<outlet property="mapView" destination="kNe-zV-9ha" id="VNR-WO-1q4"/>
</connections>
</viewController>
@@ -85,60 +99,60 @@
</connections>
</pongPressGestureRecognizer>
</objects>
- <point key="canvasLocation" x="1366" y="350"/>
+ <point key="canvasLocation" x="1365.5999999999999" y="349.47526236881561"/>
</scene>
<!--Offline Packs-->
<scene sceneID="xIg-PA-7r3">
<objects>
<tableViewController id="7q0-lI-zqb" customClass="MBXOfflinePacksTableViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="eeN-6b-zqe">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Inactive" editingAccessoryType="detailDisclosureButton" textLabel="JtH-Ce-MI5" detailTextLabel="tTJ-jv-U9v" style="IBUITableViewCellStyleSubtitle" id="fGu-Ys-Eh1">
- <rect key="frame" x="0.0" y="92" width="600" height="44"/>
+ <rect key="frame" x="0.0" y="92" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fGu-Ys-Eh1" id="sUf-bc-8xG">
- <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+ <frame key="frameInset" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Inactive Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JtH-Ce-MI5">
- <rect key="frame" x="15" y="6" width="174.5" height="19.5"/>
+ <frame key="frameInset" minX="15" minY="6" width="174.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="456 resources (789 MB)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tTJ-jv-U9v">
- <rect key="frame" x="15" y="25.5" width="128" height="13.5"/>
+ <frame key="frameInset" minX="15" minY="25.5" width="128" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Active" editingAccessoryType="detailDisclosureButton" textLabel="9ZK-gS-wJ4" detailTextLabel="0xK-p8-Mmh" style="IBUITableViewCellStyleSubtitle" id="mKB-tz-Zfl">
- <rect key="frame" x="0.0" y="136" width="600" height="44"/>
+ <rect key="frame" x="0.0" y="136" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mKB-tz-Zfl" id="nS3-aU-nBr">
- <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+ <frame key="frameInset" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Active Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9ZK-gS-wJ4">
- <rect key="frame" x="15" y="6" width="163" height="19.5"/>
+ <frame key="frameInset" minX="15" minY="6" width="163" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Downloading 123 of 456 resources… (789 MB downloaded)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0xK-p8-Mmh">
- <rect key="frame" x="15" y="25.5" width="310.5" height="13.5"/>
+ <frame key="frameInset" minX="15" minY="25.5" width="310.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@@ -189,5 +203,5 @@
<image name="TrackingLocationOffMask.png" width="23" height="23"/>
<image name="settings.png" width="28" height="28"/>
</resources>
- <color key="tintColor" red="0.12156862745098039" green="0.5490196078431373" blue="0.6705882352941176" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="tintColor" red="0.12156862745098039" green="0.5490196078431373" blue="0.6705882352941176" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</document>
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 24a69e1d5a..9206036a66 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -129,7 +129,10 @@ enum { MGLAnnotationTagNotFound = UINT32_MAX };
/// Mapping from an annotation tag to metadata about that annotation, including
/// the annotation itself.
-typedef std::map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationContextMap;
+typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap;
+
+/// Mapping from an annotation object to an annotation tag.
+typedef std::map<id<MGLAnnotation>, MGLAnnotationTag> MGLAnnotationObjectTagMap;
/// Initializes the run loop shim that lives on the main thread.
void MGLinitializeRunLoop() {
@@ -254,6 +257,7 @@ public:
@property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement;
@property (nonatomic) MGLAnnotationContainerView *annotationContainerView;
@property (nonatomic) MGLUserLocation *userLocation;
+@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *annotationViewReuseQueueByIdentifier;
@end
@@ -267,10 +271,11 @@ public:
NS_MUTABLE_ARRAY_OF(NSURL *) *_bundledStyleURLs;
- MGLAnnotationContextMap _annotationContextsByAnnotationTag;
+ MGLAnnotationTagContextMap _annotationContextsByAnnotationTag;
+ MGLAnnotationObjectTagMap _annotationTagsByAnnotation;
+
/// Tag of the selected annotation. If the user location annotation is selected, this ivar is set to `MGLAnnotationTagNotFound`.
MGLAnnotationTag _selectedAnnotationTag;
- NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *_annotationViewReuseQueueByIdentifier;
BOOL _userLocationAnnotationIsSelected;
/// Size of the rectangle formed by unioning the maximum slop area around every annotation image and annotation image view.
@@ -428,6 +433,7 @@ public:
// Set up annotation management and selection state.
_annotationImagesByIdentifier = [NSMutableDictionary dictionary];
_annotationContextsByAnnotationTag = {};
+ _annotationTagsByAnnotation = {};
_annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary];
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
@@ -2987,6 +2993,7 @@ public:
context.viewReuseIdentifier = annotationView.reuseIdentifier;
}
+ _annotationTagsByAnnotation[annotation] = annotationTag;
_annotationContextsByAnnotationTag[annotationTag] = context;
if ([annotation isKindOfClass:[NSObject class]]) {
@@ -3083,9 +3090,7 @@ public:
- (nullable MGLAnnotationView *)viewForAnnotation:(id<MGLAnnotation>)annotation
{
- MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation];
- if (annotationTag == MGLAnnotationTagNotFound) return nil;
-
+ MGLAnnotationTag annotationTag = _annotationTagsByAnnotation.at(annotation);
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
return annotationContext.annotationView;
}
@@ -3186,6 +3191,16 @@ public:
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
MGLAnnotationView *annotationView = annotationContext.annotationView;
+
+ if (annotationContext.viewReuseIdentifier)
+ {
+ NSMutableArray *annotationViewReuseQueue = [self annotationViewReuseQueueForIdentifier:annotationContext.viewReuseIdentifier];
+ if (![annotationViewReuseQueue containsObject:annotationView])
+ {
+ [annotationViewReuseQueue removeObject:annotationView];
+ }
+ }
+
annotationView.annotation = nil;
[annotationView removeFromSuperview];
@@ -3195,6 +3210,7 @@ public:
}
_annotationContextsByAnnotationTag.erase(annotationTag);
+ _annotationTagsByAnnotation.erase(annotation);
if ([annotation isKindOfClass:[NSObject class]] && ![annotation isKindOfClass:[MGLMultiPoint class]])
{
@@ -4629,49 +4645,89 @@ public:
[CATransaction begin];
[CATransaction setDisableActions:YES];
- for (auto &pair : _annotationContextsByAnnotationTag)
+ // If the map is pitched consider the viewport to be exactly the same as the bounds.
+ // Otherwise, add a small buffer.
+ CGFloat widthAdjustment = self.camera.pitch > 0.0 ? 0.0 : -_largestAnnotationViewSize.width * 2.0;
+ CGFloat heightAdjustment = self.camera.pitch > 0.0 ? 0.0 : -_largestAnnotationViewSize.height * 2.0;
+ CGRect viewPort = CGRectInset(self.bounds, widthAdjustment, heightAdjustment);
+
+ NSArray *visibleAnnotations = [self visibleAnnotationsInRect:viewPort];
+ NSMutableArray *offscreenAnnotations = [self.annotations mutableCopy];
+ [offscreenAnnotations removeObjectsInArray:visibleAnnotations];
+
+ // Update the center of visible annotation views
+ for (id<MGLAnnotation> annotation in visibleAnnotations)
{
- CGRect viewPort = CGRectInset(self.bounds,
- -_largestAnnotationViewSize.width / 2.0 - MGLAnnotationUpdateViewportOutset.width / 2.0,
- -_largestAnnotationViewSize.height / 2.0 - MGLAnnotationUpdateViewportOutset.width);
-
- MGLAnnotationContext &annotationContext = pair.second;
- MGLAnnotationView *annotationView = annotationContext.annotationView;
-
// Defer to the shape/polygon styling delegate methods
- if ([annotationContext.annotation isKindOfClass:[MGLMultiPoint class]])
+ if ([annotation isKindOfClass:[MGLMultiPoint class]])
{
continue;
}
+ // Get the annotation tag then use it to get the context. This avoids the expensive lookup
+ // by tag in `annotationTagForAnnotation:`
+ MGLAnnotationTag annotationTag = _annotationTagsByAnnotation.at(annotation);
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
if (!annotationView)
{
+ // This will dequeue views if the delegate implements the dequeue call
MGLAnnotationView *annotationView = [self annotationViewForAnnotation:annotationContext.annotation];
+
if (annotationView)
{
annotationView.mapView = self;
- annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
annotationContext.annotationView = annotationView;
+ // New annotation (created because there is nothing to dequeue) may not have been added to the
+ // container view yet. Add them here.
if (!annotationView.superview) {
[self.annotationContainerView insertSubview:annotationView atIndex:0];
}
}
- else
- {
- // if there is no annotationView at this point then we are dealing with a sprite backed annotation
- continue;
- }
}
- bool annotationViewIsVisible = CGRectContainsRect(viewPort, annotationView.frame);
- if (!annotationViewIsVisible && annotationContext.viewReuseIdentifier)
+ if (annotationView)
{
- [self enqueueAnnotationViewForAnnotationContext:annotationContext];
+ annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
}
- else
+ }
+
+ CGPoint upperLeft = {_largestAnnotationViewSize.width,_largestAnnotationViewSize.height};
+ CGPoint lowerRight = {CGRectGetWidth(self.bounds) + _largestAnnotationViewSize.width,
+ CGRectGetHeight(self.bounds) + _largestAnnotationViewSize.height};
+
+ CLLocationCoordinate2D upperLeftCoordinate = [self convertPoint:upperLeft toCoordinateFromView:self];
+ CLLocationCoordinate2D lowerRightCoordinate = [self convertPoint:lowerRight toCoordinateFromView:self];
+
+ // Enqueue (and move if required) offscreen annotation views
+ for (id<MGLAnnotation> annotation in offscreenAnnotations)
+ {
+ CLLocationCoordinate2D coordinate = annotation.coordinate;
+ MGLAnnotationTag annotationTag = _annotationTagsByAnnotation.at(annotation);
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+ UIView *annotationView = annotationContext.annotationView;
+
+ if (annotationView)
{
- annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
+ // Every so often (1 out of 1000 frames?) the mbgl query mechanism fails. This logic spot checks the
+ // offscreenAnnotations values -- if they are actually still on screen then the view center is
+ // moved and the enqueue operation is avoided. This allows us to keep the performance benefit of
+ // using the mbgl query result. It also forces views that have just gone offscreen to be cleared
+ // fully from view.
+ if ((coordinate.latitude > upperLeftCoordinate.latitude || coordinate.latitude < lowerRightCoordinate.latitude) ||
+ (coordinate.longitude < upperLeftCoordinate.longitude || coordinate.longitude > lowerRightCoordinate.longitude))
+ {
+ CGRect adjustedFrame = annotationView.frame;
+ adjustedFrame.origin.x = -CGRectGetWidth(adjustedFrame) * 2.0;
+ annotationView.frame = adjustedFrame;
+ [self enqueueAnnotationViewForAnnotationContext:annotationContext];
+ }
+ else
+ {
+ annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
+ }
}
}
@@ -4684,10 +4740,9 @@ public:
if (!annotationView) return;
- annotationView.annotation = nil;
-
if (annotationContext.viewReuseIdentifier)
{
+ annotationView.annotation = nil;
NSMutableArray *annotationViewReuseQueue = [self annotationViewReuseQueueForIdentifier:annotationContext.viewReuseIdentifier];
if (![annotationViewReuseQueue containsObject:annotationView])
{
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 4d1f00359d..4df365378a 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -108,7 +108,7 @@ enum { MGLAnnotationTagNotFound = UINT32_MAX };
/// Mapping from an annotation tag to metadata about that annotation, including
/// the annotation itself.
-typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationContextMap;
+typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap;
/// Returns an NSImage for the default marker image.
NSImage *MGLDefaultMarkerImage() {
@@ -171,7 +171,7 @@ public:
CGFloat _pitchAtBeginningOfGesture;
BOOL _didHideCursorDuringGesture;
- MGLAnnotationContextMap _annotationContextsByAnnotationTag;
+ MGLAnnotationTagContextMap _annotationContextsByAnnotationTag;
MGLAnnotationTag _selectedAnnotationTag;
MGLAnnotationTag _lastSelectedAnnotationTag;
/// Size of the rectangle formed by unioning the maximum slop area around every annotation image.