summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorJesse Bounds <jesse@rebounds.net>2016-05-26 23:10:02 -0700
committerJesse Bounds <jesse@rebounds.net>2016-05-26 23:10:02 -0700
commitc90f51a1a9ae30e16514b42478064b8ee06a68a6 (patch)
treedafd0e6ee811cfcf70ba941507a477d32088674d /platform
parent8ace0d37d10e8174268f259dec38cf88e2494ef0 (diff)
downloadqtlocation-mapboxgl-c90f51a1a9ae30e16514b42478064b8ee06a68a6.tar.gz
[ios] Add annotation view scaling (#5085)
This adds an option to MGLAnnotationView so that a developer can control how their annotation views scale in size as they approach the top of the map view (along the Y axis). The size change is also affected by the pitch (tilt) of the map view. If there is no tilt then scaling is not performed. As the map is tilted scaling is gradually applied. This turns the MGLAnnotationView implementation into an Objective C++ class so that SDK constants from C++ files can be used. The new behavior is enabled by default to maintain consistency with annotations backed by GL sprites. The MBXViewController redundantly sets the new property as an illustration of use.
Diffstat (limited to 'platform')
-rw-r--r--platform/ios/app/MBXViewController.m1
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj12
-rw-r--r--platform/ios/src/MGLAnnotationView.h11
-rw-r--r--platform/ios/src/MGLAnnotationView.m69
-rw-r--r--platform/ios/src/MGLAnnotationView.mm105
5 files changed, 121 insertions, 77 deletions
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 414329b437..13b4fbffec 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -556,6 +556,7 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
annotationView.frame = CGRectMake(0, 0, 40, 40);
annotationView.centerColor = [UIColor whiteColor];
annotationView.flat = YES;
+ annotationView.scalesWithViewingDistance = YES;
} 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 6271fc455b..cf48b869e3 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -7,8 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.m */; };
- 4018B1C81CDC287F00F666AF /* MGLAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.m */; };
+ 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
+ 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
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, ); }; };
@@ -311,7 +311,7 @@
/* Begin PBXFileReference section */
4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView_Private.h; sourceTree = "<group>"; };
- 4018B1C41CDC277F00F666AF /* MGLAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationView.m; sourceTree = "<group>"; };
+ 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>"; };
40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
@@ -714,7 +714,7 @@
isa = PBXGroup;
children = (
4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */,
- 4018B1C41CDC277F00F666AF /* MGLAnnotationView.m */,
+ 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */,
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */,
DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */,
DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */,
@@ -1289,7 +1289,7 @@
DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */,
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.m in Sources */,
+ 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
@@ -1327,7 +1327,7 @@
DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */,
- 4018B1C81CDC287F00F666AF /* MGLAnnotationView.m in Sources */,
+ 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
DAA4E4341CBB730400178DFB /* MGLUserLocationAnnotationView.m in Sources */,
DAA4E42C1CBB730400178DFB /* reachability.m in Sources */,
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 0762286b02..5b8091e7b4 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -11,9 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
Initializes and returns a new annotation view object.
@param reuseIdentifier The string that identifies that this annotation view is reusable.
- @return The initialized annotation view object or `nil` if there was a problem initializing the object.
+ @return The initialized annotation view object.
*/
-- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
+- (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier;
/**
The string that identifies that this annotation view is reusable. (read-only)
@@ -40,6 +40,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign, getter=isFlat) BOOL flat;
+/**
+ Setting this property to YES will cause the annotation view to shrink as it approaches the horizon and grow as it moves away from the
+ horizon when the associated map view is tilted. Conversely, setting this property to NO will ensure that the annotation view maintains
+ a constant size even when the map view is tilted. To maintain consistency with annotation representations that are not backed by an
+ MGLAnnotationView object, the default value of this property is YES.
+ */
+@property (nonatomic, assign, getter=isScaledWithViewingDistance) BOOL scalesWithViewingDistance;
/**
Called when the view is removed from the reuse queue.
diff --git a/platform/ios/src/MGLAnnotationView.m b/platform/ios/src/MGLAnnotationView.m
deleted file mode 100644
index 5fcfe2651d..0000000000
--- a/platform/ios/src/MGLAnnotationView.m
+++ /dev/null
@@ -1,69 +0,0 @@
-#import "MGLAnnotationView.h"
-#import "MGLAnnotationView_Private.h"
-#import "MGLMapView.h"
-
-@interface MGLAnnotationView ()
-
-@property (nonatomic) id<MGLAnnotation> annotation;
-@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
-@end
-
-@implementation MGLAnnotationView
-
-- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
-{
- self = [super init];
-
- if (self)
- {
- _reuseIdentifier = [reuseIdentifier copy];
- }
-
- return self;
-}
-
-- (void)prepareForReuse
-{
- // Intentionally left blank. The default implementation of this method does nothing.
-}
-
-- (void)setCenterOffset:(CGVector)centerOffset
-{
- _centerOffset = centerOffset;
- self.center = self.center;
-}
-
-- (void)setCenter:(CGPoint)center
-{
- [self setCenter:center pitch:0];
-}
-
-- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch
-{
- center.x += _centerOffset.dx;
- center.y += _centerOffset.dy;
-
- [super setCenter:center];
-
- if (_flat) {
- [self updatePitch:pitch];
- }
-}
-
-- (void)updatePitch:(CGFloat)pitch
-{
- CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(pitch), 1.0, 0, 0);
- self.layer.transform = t;
-}
-
-- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
-{
- // Allow mbgl to drive animation of this view’s bounds.
- if ([event isEqualToString:@"bounds"])
- {
- return [NSNull null];
- }
- return [super actionForLayer:layer forKey:event];
-}
-
-@end \ No newline at end of file
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
new file mode 100644
index 0000000000..1574da81b2
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -0,0 +1,105 @@
+#import "MGLAnnotationView.h"
+#import "MGLAnnotationView_Private.h"
+#import "MGLMapView.h"
+
+#include <mbgl/util/constants.hpp>
+
+@interface MGLAnnotationView ()
+
+@property (nonatomic) id<MGLAnnotation> annotation;
+@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
+@end
+
+@implementation MGLAnnotationView
+
+- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
+{
+ self = [self initWithFrame:CGRectZero];
+ if (self)
+ {
+ _reuseIdentifier = [reuseIdentifier copy];
+ _scalesWithViewingDistance = YES;
+ }
+ return self;
+}
+
+- (void)prepareForReuse
+{
+ // Intentionally left blank. The default implementation of this method does nothing.
+}
+
+- (void)setCenterOffset:(CGVector)centerOffset
+{
+ _centerOffset = centerOffset;
+ self.center = self.center;
+}
+
+- (void)setCenter:(CGPoint)center
+{
+ [self setCenter:center pitch:0];
+}
+
+- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch
+{
+ center.x += _centerOffset.dx;
+ center.y += _centerOffset.dy;
+
+ [super setCenter:center];
+
+ if (self.flat)
+ {
+ [self updatePitch:pitch];
+ }
+
+ if (self.scalesWithViewingDistance)
+ {
+ [self updateScaleForPitch:pitch];
+ }
+}
+
+- (void)updatePitch:(CGFloat)pitch
+{
+ CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(pitch), 1.0, 0, 0);
+ self.layer.transform = t;
+}
+
+- (void)updateScaleForPitch:(CGFloat)pitch
+{
+ CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
+ if (superviewHeight > 0.0) {
+ // Find the maximum amount of scale reduction to apply as the view's center moves from the top
+ // of the superview to the bottom. For example, if this view's center has moved 25% of the way
+ // from the top of the superview towards the bottom then the maximum scale reduction is 1 - .25
+ // or 75%. The range goes from a maximum of 100% to 0% as the view moves from the top to the bottom
+ // along the y axis of its superview.
+ CGFloat maxScaleReduction = 1.0 - self.center.y / superviewHeight;
+
+ // The pitch intensity represents how much the map view is actually pitched compared to
+ // what is possible. The value will range from 0% (not pitched at all) to 100% (pitched as much
+ // as the map view will allow). The map view's maximum pitch is defined in `mbgl::util::PITCH_MAX`.
+ // Since it is possible for the map view to report a pitch less than 0 due to the nature of
+ // how the gesture information is captured, the value is guarded with MAX.
+ CGFloat pitchIntensity = MAX(pitch, 0) / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
+
+ // The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
+ // multiplied by the pitch intensity. For example, if the maximum scale reduction is 75% and the
+ // map view is 50% pitched then the annotation view should be reduced by 37.5% (.75 * .5). The
+ // reduction is then normalized for a scale of 1.0.
+ CGFloat pitchAdjustedScale = 1.0 - maxScaleReduction * pitchIntensity;
+
+ CATransform3D transform = self.flat ? self.layer.transform : CATransform3DIdentity;
+ self.layer.transform = CATransform3DScale(transform, pitchAdjustedScale, pitchAdjustedScale, 1);
+ }
+}
+
+- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
+{
+ // Allow mbgl to drive animation of this view’s bounds.
+ if ([event isEqualToString:@"bounds"])
+ {
+ return [NSNull null];
+ }
+ return [super actionForLayer:layer forKey:event];
+}
+
+@end \ No newline at end of file