Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion platform/darwin/src/MGLAnnotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
The center point (specified as a map coordinate) of the annotation. (required)
(read-only)
*/
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
Copy link
Copy Markdown
Contributor

@friedbunny friedbunny Jun 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all MGLAnnotation implementations allow for coordinate to be changed, so we’ve been making this mutable only as necessary.


@optional

Expand Down
29 changes: 29 additions & 0 deletions platform/ios/app/MBXAnnotationView.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,33 @@ - (void)setCenterColor:(UIColor *)centerColor {
}
}

- (void)setDragState:(MGLAnnotationViewDragState)dragState animated:(BOOL)animated
{
[super setDragState:dragState animated:NO];

switch (dragState) {
case MGLAnnotationViewDragStateNone:
break;
case MGLAnnotationViewDragStateStarting: {
[UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:.4 initialSpringVelocity:.5 options:UIViewAnimationOptionCurveLinear animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2, 2);
} completion:nil];
break;
}
case MGLAnnotationViewDragStateDragging:
break;
case MGLAnnotationViewDragStateCanceling:
break;
case MGLAnnotationViewDragStateEnding: {
[UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:.4 initialSpringVelocity:.5 options:UIViewAnimationOptionCurveLinear animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
} completion:nil];
break;
}
}

}



@end
10 changes: 8 additions & 2 deletions platform/ios/app/MBXViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ - (void)presentAnnotationWithCustomCallout

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)longPress
{
/*
if (longPress.state == UIGestureRecognizerStateBegan)
{
CGPoint point = [longPress locationInView:longPress.view];
Expand All @@ -427,7 +428,7 @@ - (IBAction)handleLongPress:(UILongPressGestureRecognizer *)longPress
pin.subtitle = [[[MGLCoordinateFormatter alloc] init] stringFromCoordinate:pin.coordinate];
// Calling `addAnnotation:` on mapView is not required since `selectAnnotation:animated` has the side effect of adding the annotation if required
[self.mapView selectAnnotation:pin animated:YES];
}
}*/
}

- (IBAction)cycleStyles:(__unused id)sender
Expand Down Expand Up @@ -586,7 +587,12 @@ - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAn
annotationView = [[MBXAnnotationView alloc] initWithReuseIdentifier:MBXViewControllerAnnotationViewReuseIdentifer];
annotationView.frame = CGRectMake(0, 0, 10, 10);
annotationView.centerColor = [UIColor whiteColor];


// uncomment to make the annotation view draggable
// also note that having two long press gesture recognizers on overlapping views (`self.view` & `annotationView`) will cause weird behaviour
// comment out the pin dropping functionality in the handleLongPress: 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
// this currently causes severe performance issues when more than 2k annotations are visible
// annotationView.flat = YES;
Expand Down
25 changes: 25 additions & 0 deletions platform/ios/src/MGLAnnotationView.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
MGLAnnotationViewDragStateNone = 0, // View is sitting on the map
MGLAnnotationViewDragStateStarting, // View is beginning to drag and animation starts
MGLAnnotationViewDragStateDragging, // View is being dragged
MGLAnnotationViewDragStateCanceling, // View dragging was cancelled and will be returned to its starting positon.
MGLAnnotationViewDragStateEnding // View was dragged, new coordinate has been set and stop animation is finishing
};

/** The MGLAnnotationView class is responsible for representing point-based annotation markers as a view. Annotation views represent an annotation object, which is an object that corresponds to the MGLAnnotation protocol. When an annotation’s coordinate point is visible on the map view, the map view delegate is asked to provide a corresponding annotation view. If an annotation view is created with a reuse identifier, the map view may recycle the view when it goes offscreen. */
@interface MGLAnnotationView : UIView

Expand Down Expand Up @@ -48,6 +56,23 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign, getter=isScaledWithViewingDistance) BOOL scalesWithViewingDistance;

/**
Setting this property to YES will make the view draggable. Long press followed by a pan gesture will start to move the
view around the map. Each update will center the annotation view. setCoordinate: is called on the associated annotation when panning ends successfully.
*/
@property (nonatomic, assign, getter=isDraggable) BOOL draggable;

/**
All states are handled automatically when `draggable` is set to YES.
Custom animations can be achieved by overriding setDragState:animated:
*/
@property (nonatomic, readonly) MGLAnnotationViewDragState dragState;

/**
Implementer may override this method to use a custom animation for the different MGLAnnotationViewDragState.
*/
- (void)setDragState:(MGLAnnotationViewDragState)dragState animated:(BOOL)animated __attribute__((objc_requires_super));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use NS_REQUIRES_SUPER instead of the raw attribute.


/**
Called when the view is removed from the reuse queue.

Expand Down
133 changes: 132 additions & 1 deletion platform/ios/src/MGLAnnotationView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

#include <mbgl/util/constants.hpp>

@interface MGLAnnotationView ()
@interface MGLAnnotationView () <UIGestureRecognizerDelegate>

@property (nonatomic) id<MGLAnnotation> annotation;
@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
@property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer;
@property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer;
@property (nonatomic, weak) MGLMapView *mapView;

@end

Expand Down Expand Up @@ -49,6 +52,11 @@ - (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch

[super setCenter:center];

// Omit applying a new transformation while the view is being dragged.
if (self.dragState == MGLAnnotationViewDragStateDragging) {
return;
}

if (self.flat)
{
[self updatePitch:pitch];
Expand Down Expand Up @@ -95,6 +103,129 @@ - (void)updateScaleForPitch:(CGFloat)pitch
}
}

- (void)setDraggable:(BOOL)draggable
{
_draggable = draggable;
if (draggable) {
[self enableDrag];
} else {
[self disableDrag];
}
}

- (void)enableDrag
{
if (!_longPressRecognizer) {
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
recognizer.delegate = self;
[self addGestureRecognizer:recognizer];
_longPressRecognizer = recognizer;
}

if (!_panGestureRecognizer) {
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
recognizer.delegate = self;
[self addGestureRecognizer:recognizer];
_panGestureRecognizer = recognizer;
}
}

- (void)disableDrag
{
[self removeGestureRecognizer:_longPressRecognizer];
[self removeGestureRecognizer:_panGestureRecognizer];
}

- (void)handleLongPress:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
self.dragState = MGLAnnotationViewDragStateStarting;
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
self.dragState = MGLAnnotationViewDragStateDragging;
}
else if (sender.state == UIGestureRecognizerStateCancelled) {
self.dragState = MGLAnnotationViewDragStateCanceling;
}
else if (sender.state == UIGestureRecognizerStateEnded)
{
self.dragState = MGLAnnotationViewDragStateEnding;
}
}

- (void)handlePan:(UIPanGestureRecognizer *)sender
{
CGPoint center = [sender locationInView:sender.view.superview];
[self setCenter:center pitch:self.mapView.camera.pitch];

if (sender.state == UIGestureRecognizerStateEnded) {
self.dragState = MGLAnnotationViewDragStateNone;
}
}

- (void)setDragState:(MGLAnnotationViewDragState)dragState
{
[self setDragState:dragState animated:YES];
}

- (void)setDragState:(MGLAnnotationViewDragState)dragState animated:(BOOL)animated
{
_dragState = dragState;

switch (dragState) {
case MGLAnnotationViewDragStateNone:
break;
case MGLAnnotationViewDragStateStarting: {
if (animated)
{
[UIView animateWithDuration:.20 animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2, 2);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does -[MKAnnotationView setDragState:animated:] have any built-in animation like this? I thought only MKPinAnnotationView implements that method, while MKAnnotationView only has it as a stub. Putting a built-in animation here means that a draggable annotation view always behaves like a pin, even if it doesn’t look like one. Maybe we need to finally create an MGLPinAnnotationView (which would be the ideal place to implement #2250).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely right @1ec5 the animation stuff should not be included in MGLAnnotationView

}];
}
break;
}
case MGLAnnotationViewDragStateDragging:
break;
case MGLAnnotationViewDragStateCanceling:
break;
case MGLAnnotationViewDragStateEnding: {

CLLocationCoordinate2D coord = [self.mapView convertPoint:self.center toCoordinateFromView:self.mapView];
[self.annotation setCoordinate:coord];

if (animated)
{
[UIView animateWithDuration:.20 animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
} completion:^(BOOL finished) {
self.dragState = MGLAnnotationViewDragStateNone;
}];
}
else
{
_dragState = MGLAnnotationViewDragStateNone;
}
break;
}
}
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
BOOL isDragging = self.dragState == MGLAnnotationViewDragStateDragging;
if ([gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class] && !(isDragging)) {
return NO;
}
return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
// Allow mbgl to drive animation of this view’s bounds.
Expand Down
3 changes: 3 additions & 0 deletions platform/ios/src/MGLAnnotationView_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

NS_ASSUME_NONNULL_BEGIN

@class MGLMapView;

@interface MGLAnnotationView (Private)

@property (nonatomic) id<MGLAnnotation> annotation;
@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
@property (nonatomic, weak) MGLMapView *mapView;

- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch;

Expand Down
1 change: 1 addition & 0 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4507,6 +4507,7 @@ - (void)updateAnnotationViews
else
{
CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
annotationView.mapView = self;
[annotationView setCenter:center pitch:self.camera.pitch];
}
}
Expand Down