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

Animate the user dot #1582

Merged
merged 1 commit into from
Apr 18, 2016
Merged
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
1 change: 1 addition & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Mapbox welcomes participation and contributions from everyone. If you’d like

- Applications linking against the SDK static framework no longer need to add `-ObjC` to the Other Linker Flags (`OTHER_LDFLAGS`) build setting. If you previously added this flag solely for this SDK, removing the flag may potentially reduce the overall size of your application. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
- Removed the `armv7s` slice from the SDK to reduce its size. iPhone 5 and iPhone 5c automatically use the `armv7` slice instead. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
- The user dot now moves smoothly between user location updates while user location tracking is disabled. ([#1582](https://github.com/mapbox/mapbox-gl-native/pull/1582))
- User location heading updates now resume properly when an app becomes active again. ([#4674](https://github.com/mapbox/mapbox-gl-native/pull/4674))
- A more specific user agent string is now sent with style and tile requests. ([#4012](https://github.com/mapbox/mapbox-gl-native/pull/4012))
- Removed unused SVG files from the SDK’s resource bundle. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
Expand Down
5 changes: 5 additions & 0 deletions platform/ios/app/MBXCustomCalloutView.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ - (instancetype)initWithFrame:(CGRect)frame

- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated
{
if ([self.delegate respondsToSelector:@selector(calloutViewWillAppear:)])
{
[self.delegate performSelector:@selector(calloutViewWillAppear:) withObject:self];
}

[view addSubview:self];
// prepare title label
if ([self.representedObject respondsToSelector:@selector(title)])
Expand Down
5 changes: 5 additions & 0 deletions platform/ios/include/MGLCalloutView.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)calloutViewTapped:(UIView<MGLCalloutView> *)calloutView;

/**
Called before the callout view appears on screen, or before the appearance animation will start.
*/
- (void)calloutViewWillAppear:(UIView<MGLCalloutView> *)calloutView;

@end

NS_ASSUME_NONNULL_END
100 changes: 80 additions & 20 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ @implementation MGLMapView
/// Size of the rectangle formed by unioning the maximum slop area around every annotation image.
CGSize _unionedAnnotationImageSize;
std::vector<MGLAnnotationTag> _annotationsNearbyLastTap;
CGPoint _initialImplicitCalloutViewOffset;
NSDate *_userLocationAnimationCompletionDate;

BOOL _isWaitingForRedundantReachableNotification;
BOOL _isTargetingInterfaceBuilder;
Expand Down Expand Up @@ -1268,21 +1270,14 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap

CGPoint tapPoint = [singleTap locationInView:self];

if (self.userLocationVisible)
if (self.userLocationVisible
&& [self.userLocationAnnotationView.layer.presentationLayer hitTest:tapPoint])
{
// Assume that the user is fat-fingering an annotation.
CGRect hitRect = CGRectInset({ tapPoint, CGSizeZero },
-MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);

if (CGRectIntersectsRect(hitRect, self.userLocationAnnotationView.frame))
if ( ! _userLocationAnnotationIsSelected)
{
if ( ! _userLocationAnnotationIsSelected)
{
[self selectAnnotation:self.userLocation animated:YES];
}
return;
[self selectAnnotation:self.userLocation animated:YES];
}
return;
}

MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:tapPoint persistingResults:YES];
Expand Down Expand Up @@ -2830,9 +2825,12 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated

if (_userLocationAnnotationIsSelected)
{
positioningRect = CGRectInset(self.userLocationAnnotationView.frame,
-MGLAnnotationImagePaddingForCallout,
-MGLAnnotationImagePaddingForCallout);
positioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame];

CGRect implicitAnnotationFrame = [self.userLocationAnnotationView.layer.presentationLayer frame];
CGRect explicitAnnotationFrame = self.userLocationAnnotationView.frame;
_initialImplicitCalloutViewOffset = CGPointMake(CGRectGetMinX(explicitAnnotationFrame) - CGRectGetMinX(implicitAnnotationFrame),
CGRectGetMinY(explicitAnnotationFrame) - CGRectGetMinY(implicitAnnotationFrame));
}

// consult delegate for left and/or right accessory views
Expand Down Expand Up @@ -2940,7 +2938,7 @@ - (void)deselectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animate
{
if ( ! annotation) return;

if ([self.selectedAnnotation isEqual:annotation])
if (self.selectedAnnotation == annotation)
{
// dismiss popup
[self.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated];
Expand All @@ -2957,6 +2955,35 @@ - (void)deselectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animate
}
}

- (void)calloutViewWillAppear:(UIView <MGLCalloutView> *)calloutView
{
if (_userLocationAnnotationIsSelected ||
CGPointEqualToPoint(_initialImplicitCalloutViewOffset, CGPointZero))
{
return;
}

// The user location callout view initially points to the user location
// annotation’s implicit (visual) frame, which is offset from the
// annotation’s explicit frame. Now the callout view needs to rendezvous
// with the explicit frame. Then,
// -updateUserLocationAnnotationViewAnimatedWithDuration: will take over the
// next time an updated location arrives.
[UIView animateWithDuration:_userLocationAnimationCompletionDate.timeIntervalSinceNow
delay:0
options:(UIViewAnimationOptionCurveLinear |
UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState)
animations:^
{
calloutView.frame = CGRectOffset(calloutView.frame,
_initialImplicitCalloutViewOffset.x,
_initialImplicitCalloutViewOffset.y);
_initialImplicitCalloutViewOffset = CGPointZero;
}
completion:NULL];
}

- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated
{
CGFloat maximumPadding = 100;
Expand Down Expand Up @@ -3269,7 +3296,12 @@ - (void)locationManager:(__unused CLLocationManager *)manager didUpdateLocations
self.userLocationAnnotationView.haloLayer.hidden = ! CLLocationCoordinate2DIsValid(self.userLocation.coordinate) ||
newLocation.horizontalAccuracy > 10;

[self updateUserLocationAnnotationView];
NSTimeInterval duration = MGLAnimationDuration;
if (oldLocation && ! CGPointEqualToPoint(self.userLocationAnnotationView.center, CGPointZero))
{
duration = MAX([newLocation.timestamp timeIntervalSinceDate:oldLocation.timestamp], MGLUserLocationAnimationDuration);
}
[self updateUserLocationAnnotationViewAnimatedWithDuration:duration];
}

- (void)didUpdateLocationWithUserTrackingAnimated:(BOOL)animated
Expand Down Expand Up @@ -3709,15 +3741,18 @@ - (void)notifyMapChange:(mbgl::MapChange)change
}

- (void)updateUserLocationAnnotationView
{
[self updateUserLocationAnnotationViewAnimatedWithDuration:0];
}

- (void)updateUserLocationAnnotationViewAnimatedWithDuration:(NSTimeInterval)duration
{
MGLUserLocationAnnotationView *annotationView = self.userLocationAnnotationView;
if ( ! CLLocationCoordinate2DIsValid(self.userLocation.coordinate)) {
annotationView.hidden = YES;
return;
}

if ( ! annotationView.superview) [self.glView addSubview:annotationView];

CGPoint userPoint;
if (self.userTrackingMode != MGLUserTrackingModeNone
&& self.userTrackingState == MGLUserTrackingStateChanged)
Expand All @@ -3728,11 +3763,36 @@ - (void)updateUserLocationAnnotationView
{
userPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self];
}

if ( ! annotationView.superview)
{
[self.glView addSubview:annotationView];
// Prevents the view from sliding in from the origin.
annotationView.center = userPoint;
}

if (CGRectContainsPoint(CGRectInset(self.bounds, -MGLAnnotationUpdateViewportOutset.width,
-MGLAnnotationUpdateViewportOutset.height), userPoint))
{
annotationView.center = userPoint;
// Smoothly move the user location annotation view and callout view to
// the new location.
[UIView animateWithDuration:duration
delay:0
options:(UIViewAnimationOptionCurveLinear |
UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState)
animations:^{
if (self.selectedAnnotation == self.userLocation)
{
UIView <MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation;
calloutView.frame = CGRectOffset(calloutView.frame,
userPoint.x - annotationView.center.x,
userPoint.y - annotationView.center.y);
}
annotationView.center = userPoint;
} completion:NULL];
_userLocationAnimationCompletionDate = [NSDate dateWithTimeIntervalSinceNow:duration];

annotationView.hidden = NO;
[annotationView setupLayers];

Expand Down