diff options
author | Jesse Bounds <jesse@rebounds.net> | 2016-06-01 17:36:07 -0700 |
---|---|---|
committer | Jesse Bounds <jesse@rebounds.net> | 2016-06-01 17:36:07 -0700 |
commit | d9c6181f347c3d6b3cfd4bca7c5ded0d6d93e63d (patch) | |
tree | cbe8e24f775a45dd722189bf79c3281b3ec3910b /platform | |
parent | ce98a7bee7709cd07e7f064215474903a90a0836 (diff) | |
download | qtlocation-mapboxgl-d9c6181f347c3d6b3cfd4bca7c5ded0d6d93e63d.tar.gz |
[ios] Add annotation container view (#5194)
Add a container view to hold annotations. This gets around a performance
issue with `UIView:addSubview:` where adding views is N^2. It helps
annotation views avoid cutting into callout views when the annotation
views are transformed to be "flat".
Diffstat (limited to 'platform')
-rw-r--r-- | platform/ios/app/MBXAnnotationView.m | 2 | ||||
-rw-r--r-- | platform/ios/app/MBXViewController.m | 20 | ||||
-rw-r--r-- | platform/ios/ios.xcodeproj/project.pbxproj | 12 | ||||
-rw-r--r-- | platform/ios/src/MGLAnnotationContainerView.h | 17 | ||||
-rw-r--r-- | platform/ios/src/MGLAnnotationContainerView.m | 38 | ||||
-rw-r--r-- | platform/ios/src/MGLAnnotationView.mm | 1 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 31 |
7 files changed, 114 insertions, 7 deletions
diff --git a/platform/ios/app/MBXAnnotationView.m b/platform/ios/app/MBXAnnotationView.m index 8cbe07a367..890881a316 100644 --- a/platform/ios/app/MBXAnnotationView.m +++ b/platform/ios/app/MBXAnnotationView.m @@ -12,7 +12,7 @@ [super layoutSubviews]; if (!self.centerView) { self.backgroundColor = [UIColor blueColor]; - self.centerView = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 5.0, 5.0)]; + self.centerView = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 1.0, 1.0)]; self.centerView.backgroundColor = self.centerColor; [self addSubview:self.centerView]; } diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index c72bdfd0f8..70f777422d 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -574,14 +574,28 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation { + // Use GL backed pins for dropped pin annotations + if ([annotation isMemberOfClass:[MBXDroppedPinAnnotation class]]) + { + return nil; + } + MBXAnnotationView *annotationView = (MBXAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:MBXViewControllerAnnotationViewReuseIdentifer]; if (!annotationView) { annotationView = [[MBXAnnotationView alloc] initWithReuseIdentifier:MBXViewControllerAnnotationViewReuseIdentifer]; - annotationView.frame = CGRectMake(0, 0, 40, 40); + annotationView.frame = CGRectMake(0, 0, 10, 10); annotationView.centerColor = [UIColor whiteColor]; - annotationView.flat = YES; - annotationView.scalesWithViewingDistance = YES; + + // uncomment to flatten the annotation view against the map when the map is tilted + // this currently causes severe performance issues when more than 2k annotations are visible + // annotationView.flat = YES; + + // uncomment to force annotation view to maintain a constant size when the map is tilted + // by default, annotation views will shrink and grow as the move towards and away from the + // horizon. Relatedly, annotations backed by GL sprites ONLY scale with viewing distance currently. + // annotationView.scalesWithViewingDistance = NO; + } else { // orange indicates that the annotation view was reused annotationView.centerColor = [UIColor orangeColor]; diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 811fb89b7d..9c8343f890 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; }; 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; }; + 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; }; + 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; }; 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */; }; DA17BE301CC4BAC300402C41 /* MGLMapView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */; }; @@ -324,6 +327,8 @@ 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = "<group>"; }; 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; }; 402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; }; + 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = "<group>"; }; + 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; }; 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; }; 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; }; DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; }; @@ -897,9 +902,11 @@ DAD165841CF4D06B001FF4B9 /* Annotations */ = { isa = PBXGroup; children = ( + 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */, + 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */, 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */, - 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */, 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */, + 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */, DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */, DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */, DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */, @@ -977,6 +984,7 @@ DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */, DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */, DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */, + 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */, DA88484F1CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h in Headers */, DA8847F21CBAFA5100AB86E3 /* MGLMapCamera.h in Headers */, DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */, @@ -1347,6 +1355,7 @@ files = ( DA88485D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m in Sources */, DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */, + 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */, DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.m in Sources */, DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */, @@ -1387,6 +1396,7 @@ files = ( DAA4E4221CBB730400178DFB /* MGLPointAnnotation.m in Sources */, DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */, + 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */, DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */, DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */, diff --git a/platform/ios/src/MGLAnnotationContainerView.h b/platform/ios/src/MGLAnnotationContainerView.h new file mode 100644 index 0000000000..90d2964831 --- /dev/null +++ b/platform/ios/src/MGLAnnotationContainerView.h @@ -0,0 +1,17 @@ +#import <UIKit/UIKit.h> + +#import "MGLTypes.h" + +@class MGLAnnotationView; + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLAnnotationContainerView : UIView + ++ (instancetype)annotationContainerViewWithAnnotationContainerView:(MGLAnnotationContainerView *)annotationContainerView; + +- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLAnnotationContainerView.m b/platform/ios/src/MGLAnnotationContainerView.m new file mode 100644 index 0000000000..cd9c990ef0 --- /dev/null +++ b/platform/ios/src/MGLAnnotationContainerView.m @@ -0,0 +1,38 @@ +#import "MGLAnnotationContainerView.h" +#import "MGLAnnotationView.h" + +@interface MGLAnnotationContainerView () + +@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews; + +@end + +@implementation MGLAnnotationContainerView + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + _annotationViews = [NSMutableArray array]; + } + return self; +} + ++ (instancetype)annotationContainerViewWithAnnotationContainerView:(nonnull MGLAnnotationContainerView *)annotationContainerView +{ + MGLAnnotationContainerView *newAnnotationContainerView = [[MGLAnnotationContainerView alloc] initWithFrame:annotationContainerView.frame]; + [newAnnotationContainerView addSubviews:annotationContainerView.subviews]; + return newAnnotationContainerView; +} + +- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews +{ + for (MGLAnnotationView *view in subviews) + { + [self addSubview:view]; + [self.annotationViews addObject:view]; + } +} + +@end diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 1574da81b2..6f1d94c3e7 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -8,6 +8,7 @@ @property (nonatomic) id<MGLAnnotation> annotation; @property (nonatomic, readwrite, nullable) NSString *reuseIdentifier; + @end @implementation MGLAnnotationView diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 59830b094f..e5a65d42ea 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -41,6 +41,7 @@ #import "MGLAnnotationView_Private.h" #import "MGLMapboxEvents.h" #import "MGLCompactCalloutView.h" +#import "MGLAnnotationContainerView.h" #import <algorithm> #import <cstdlib> @@ -233,6 +234,7 @@ public: @property (nonatomic, getter=isDormant) BOOL dormant; @property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed; @property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement; +@property (nonatomic) MGLAnnotationContainerView *annotationContainerView; @end @@ -548,7 +550,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) _glView.delegate = self; [_glView bindDrawable]; [self insertSubview:_glView atIndex:0]; - _glView.contentMode = UIViewContentModeCenter; // load extensions @@ -2805,6 +2806,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) BOOL delegateImplementsViewForAnnotation = [self.delegate respondsToSelector:@selector(mapView:viewForAnnotation:)]; BOOL delegateImplementsImageForPoint = [self.delegate respondsToSelector:@selector(mapView:imageForAnnotation:)]; + + NSMutableArray *newAnnotationViews = [[NSMutableArray alloc] initWithCapacity:annotations.count]; for (id <MGLAnnotation> annotation in annotations) { @@ -2839,7 +2842,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { annotationViewsForAnnotation[annotationValue] = annotationView; annotationView.center = [self convertCoordinate:annotation.coordinate toPointToView:self]; - [self.glView addSubview:annotationView]; + [newAnnotationViews addObject:annotationView]; } } @@ -2927,10 +2930,34 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) } } + [self updateAnnotationContainerViewWithAnnotationViews:newAnnotationViews]; + [self didChangeValueForKey:@"annotations"]; UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); } +- (void)updateAnnotationContainerViewWithAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews +{ + if (annotationViews.count == 0) return; + + MGLAnnotationContainerView *newAnnotationContainerView; + if (self.annotationContainerView) + { + // reload any previously added views + newAnnotationContainerView = [MGLAnnotationContainerView annotationContainerViewWithAnnotationContainerView:self.annotationContainerView]; + [self.annotationContainerView removeFromSuperview]; + } + else + { + newAnnotationContainerView = [[MGLAnnotationContainerView alloc] initWithFrame:self.bounds]; + } + newAnnotationContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + newAnnotationContainerView.contentMode = UIViewContentModeCenter; + [newAnnotationContainerView addSubviews:annotationViews]; + [_glView insertSubview:newAnnotationContainerView atIndex:0]; + self.annotationContainerView = newAnnotationContainerView; +} + /// Initialize and return a default annotation image that depicts a round pin /// rising from the center, with a shadow slightly below center. The alignment /// rect therefore excludes the bottom half. |