Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[ios] Annotation view degrees of freedom
Browse files Browse the repository at this point in the history
Replaced MGLAnnotationView’s flat property with a freeAxes property that allows 0–2 degrees of freedom (pitch and/or rotation).

Reformatted and copyedited MGLAnnotationView documentation. Removed the unnecessary custom getter on scalesWithViewingDistance.

Fixes #2528.
  • Loading branch information
1ec5 committed Jul 6, 2017
1 parent ba60e9b commit cc1f1a1
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 56 deletions.
2 changes: 1 addition & 1 deletion platform/ios/Mapbox.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
let av = PlaygroundAnnotationView(reuseIdentifier: "annotation")
av.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
av.centerOffset = CGVector(dx: -15, dy: -15)
av.flat = true
av.freeAxes = .X;
let centerView = UIView(frame: CGRectInset(av.bounds, 3, 3))
centerView.backgroundColor = UIColor.whiteColor()
av.addSubview(centerView)
Expand Down
6 changes: 3 additions & 3 deletions platform/ios/app/MBXViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1711,7 +1711,7 @@ - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAn
if (!annotationView)
{
annotationView = [[MBXAnnotationView alloc] initWithReuseIdentifier:MBXViewControllerAnnotationViewReuseIdentifer];
annotationView.frame = CGRectMake(0, 0, 10, 10);
annotationView.frame = CGRectMake(0, 0, 30, 30);
annotationView.backgroundColor = [UIColor whiteColor];

// Note that having two long press gesture recognizers on overlapping
Expand All @@ -1720,9 +1720,9 @@ - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAn
// method in this class to make draggable annotation views play nice.
annotationView.draggable = YES;

// uncomment to flatten the annotation view against the map when the map is tilted
// uncomment to lay the annotation view flat against the map when the map is tilted
// this currently causes severe performance issues when more than 2k annotations are visible
// annotationView.flat = YES;
// annotationView.freeAxes = MGLAnnotationViewBillboardAxisX;

// Uncomment to force annotation view to maintain a constant size when
// the map is tilted. By default, annotation views will shrink and grow
Expand Down
86 changes: 62 additions & 24 deletions platform/ios/src/MGLAnnotationView.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,57 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
MGLAnnotationViewDragStateEnding,
};

/**
Options for locking the orientation of an `MGLAnnotationView` along one or more
axes for a billboard effect.
*/
typedef NS_OPTIONS(NSUInteger, MGLAnnotationViewBillboardAxis)
{
/**
Orients the annotation view such that its x-axis is always fixed with
respect to the map.
If this option is unset, the annotation view remains unchanged as the map’s
pitch increases, so that the view appears to stand upright on the tilted
map. If this option is set, the annotation view tilts as the map’s pitch
increases, so that the view appears to lie flat against the tilted map.
For example, you would set this option if the annotation view depicts an
arrow that should always point due south. You would unset this option if
the arrow should always point down towards the ground.
*/
MGLAnnotationViewBillboardAxisX = 0x1 << 0,

/**
Orients the annotation view such that its y-axis is always fixed with
respect to the map.
If this option is unset, the annotation view remains unchanged as the map
is rotated. If this option is set, the annotation view rotates as the map
rotates.
For example, you would set this option if the annotation view should be
aligned with a street, regardless of the direction from which the user
views the street.
*/
MGLAnnotationViewBillboardAxisY = 0x1 << 1,

/**
Orients the annotation view such that its z-axis is always fixed with
respect to the map.
Because `MGLMapView` does not support changes to its bank, or roll, this
option has no effect.
*/
MGLAnnotationViewBillboardAxisZ = 0x1 << 2,

/**
Orients the annotation view such that all three axes are always fixed with
respect to the map.
*/
MGLAnnotationViewBillboardAxisAll = (MGLAnnotationViewBillboardAxisX | MGLAnnotationViewBillboardAxisY | MGLAnnotationViewBillboardAxisZ),
};

/**
The `MGLAnnotationView` class is responsible for marking a point annotation
with a view. Annotation views represent an annotation object, which is an
Expand Down Expand Up @@ -126,8 +177,14 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
You specify the reuse identifier when you create the view. You use the
identifier later to retrieve an annotation view that was created previously but
<<<<<<< HEAD
which is currently unused because its annotation is not on-screen.
=======
which is currently unused because its annotation does not lie within the map
view’s viewport.
>>>>>>> 8d2bb2d42... [ios] Annotation view degrees of freedom
If you define distinctly different types of annotations (with distinctly
different annotation views to go with them), you can differentiate between the
annotation types by specifying different reuse identifiers for each one.
Expand Down Expand Up @@ -155,19 +212,13 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
@property (nonatomic) CGVector centerOffset;

/**
A Boolean value indicating whether the view lies flat against the map as it
tilts.
An option that specifies the annotation view’s degrees of freedom.
If this option is unset, the annotation view remains unchanged as the map’s
pitch increases, so that the view appears to stand upright on the tilted map.
If this option is set, the annotation view tilts as the map’s pitch increases,
so that the view appears to lie flat against the tilted map.
For example, you would set this option if the annotation view depicts an arrow
that should always point due south. You would unset this option if the arrow
should always point down towards the ground.
By default, none of the axes are free; in other words, the annotation view is
oriented like a billboard with respect to the x-, y-, and z-axes. See
`MGLAnnotationViewBillboardAxis` for available options.
*/
@property (nonatomic, assign, getter=isFlat) BOOL flat;
@property (nonatomic, assign) MGLAnnotationViewBillboardAxis freeAxes;

/**
A Boolean value that determines whether the annotation view grows and shrinks
Expand All @@ -186,19 +237,6 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
*/
@property (nonatomic, assign) BOOL scalesWithViewingDistance;

/**
A Boolean value that determines whether the annotation view rotates together
with the map.
When the value of this property is `YES` and the map is rotated, the annotation
view rotates. This is also the behavior of `MGLAnnotationImage` objects. When the
value of this property is `NO` the annotation has its rotation angle fixed.
The default value of this property is `NO`. Set this property to `YES` if the
view’s rotation is important.
*/
@property (nonatomic, assign) BOOL rotatesToMatchCamera;

#pragma mark Managing the Selection State

/**
Expand Down
59 changes: 31 additions & 28 deletions platform/ios/src/MGLAnnotationView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ - (instancetype)initWithCoder:(NSCoder *)decoder {
_reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"];
_annotation = [decoder decodeObjectOfClass:[NSObject class] forKey:@"annotation"];
_centerOffset = [decoder decodeCGVectorForKey:@"centerOffset"];
NSInteger freeAxes = [decoder decodeIntegerForKey:@"freeAxes"];
if (freeAxes < 0 || freeAxes > MGLAnnotationViewBillboardAxisAll) {
return nil;
}
_freeAxes = freeAxes;
_scalesWithViewingDistance = [decoder decodeBoolForKey:@"scalesWithViewingDistance"];
_rotatesToMatchCamera = [decoder decodeBoolForKey:@"rotatesToMatchCamera"];
_selected = [decoder decodeBoolForKey:@"selected"];
_enabled = [decoder decodeBoolForKey:@"enabled"];
self.draggable = [decoder decodeBoolForKey:@"draggable"];
Expand All @@ -67,8 +71,8 @@ - (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"];
[coder encodeObject:_annotation forKey:@"annotation"];
[coder encodeCGVector:_centerOffset forKey:@"centerOffset"];
[coder encodeInteger:_freeAxes forKey:@"freeAxes"];
[coder encodeBool:_scalesWithViewingDistance forKey:@"scalesWithViewingDistance"];
[coder encodeBool:_rotatesToMatchCamera forKey:@"rotatesToMatchCamera"];
[coder encodeBool:_selected forKey:@"selected"];
[coder encodeBool:_enabled forKey:@"enabled"];
[coder encodeBool:_draggable forKey:@"draggable"];
Expand Down Expand Up @@ -111,25 +115,44 @@ - (void)setCenter:(CGPoint)center
center.y += _centerOffset.dy;

super.center = center;
[self updateScaleTransformForViewingDistance];
[self updateRotateTransform];
[self updateTransform];
}

- (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance
{
if (_scalesWithViewingDistance != scalesWithViewingDistance)
{
_scalesWithViewingDistance = scalesWithViewingDistance;
[self updateScaleTransformForViewingDistance];
[self updateTransform];
}
}

- (void)setFreeAxes:(MGLAnnotationViewBillboardAxis)freeAxes
{
if (_freeAxes != freeAxes)
{
_freeAxes = freeAxes;
[self updateTransform];
}
}

- (void)updateScaleTransformForViewingDistance
- (void)updateTransform
{
if (self.scalesWithViewingDistance == NO || self.dragState == MGLAnnotationViewDragStateDragging) return;
if (self.dragState == MGLAnnotationViewDragStateDragging) return;

CATransform3D t = CATransform3DIdentity;
MGLMapCamera *camera = self.mapView.camera;
if (camera.pitch >= 0 && (self.freeAxes & MGLAnnotationViewBillboardAxisX))
{
t = CATransform3DRotate(t, MGLRadiansFromDegrees(camera.pitch), 1.0, 0, 0);
}
if (camera.heading >= 0 && (self.freeAxes & MGLAnnotationViewBillboardAxisY))
{
t = CATransform3DRotate(t, MGLRadiansFromDegrees(-camera.heading), 0.0, 0.0, 1.0);
}

CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
if (superviewHeight > 0.0) {
if ( ! self.scalesWithViewingDistance && 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
Expand Down Expand Up @@ -161,26 +184,6 @@ - (void)updateScaleTransformForViewingDistance
}
}

- (void)setRotatesToMatchCamera:(BOOL)rotatesToMatchCamera
{
if (_rotatesToMatchCamera != rotatesToMatchCamera)
{
_rotatesToMatchCamera = rotatesToMatchCamera;
[self updateRotateTransform];
}
}

- (void)updateRotateTransform
{
if (self.rotatesToMatchCamera == NO) return;

CGFloat directionRad = self.mapView.direction * M_PI / 180.0;
CATransform3D newRotateTransform = CATransform3DMakeRotation(-directionRad, 0, 0, 1);
self.layer.transform = CATransform3DConcat(CATransform3DIdentity, newRotateTransform);

_lastAppliedRotateTransform = newRotateTransform;
}

#pragma mark - Draggable

- (void)setDraggable:(BOOL)draggable
Expand Down

0 comments on commit cc1f1a1

Please sign in to comment.