diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 3541dd9a4..99fc55e7e 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -315,6 +315,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) // _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; _pan.delegate = self; + _pan.maximumNumberOfTouches = 1; [self addGestureRecognizer:_pan]; _scrollEnabled = YES; @@ -728,27 +729,22 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) { CGPoint velocity = [pan velocityInView:pan.view]; - CGFloat duration = 0; - + if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) < 100) + { + // Not enough velocity to overcome friction + velocity = CGPointZero; + } + + CGFloat duration = UIScrollViewDecelerationRateNormal; if ( ! CGPointEqualToPoint(velocity, CGPointZero)) { - CGFloat ease = 0.25; - - velocity.x = velocity.x * ease; - velocity.y = velocity.y * ease; - - CGFloat speed = sqrt(velocity.x * velocity.x + velocity.y * velocity.y); - CGFloat deceleration = 2500; - duration = speed / (deceleration * ease); + CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4); + _mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); } - CGPoint offset = CGPointMake(velocity.x * duration / 2, velocity.y * duration / 2); - - _mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); - _mbglMap->setGestureInProgress(false); - if (duration) + if ( ! CGPointEqualToPoint(velocity, CGPointZero)) { self.animatingGesture = YES; @@ -803,17 +799,60 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) if (log2(newScale) < _mbglMap->getMinZoom()) return; - double scale = _mbglMap->getScale(); - - _mbglMap->scaleBy(newScale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y); + _mbglMap->setScale(newScale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y); } else if (pinch.state == UIGestureRecognizerStateEnded || pinch.state == UIGestureRecognizerStateCancelled) { + CGFloat velocity = pinch.velocity; + if (velocity > -0.5 && velocity < 3) + { + velocity = 0; + } + CGFloat duration = velocity > 0 ? 1 : 0.25; + + CGFloat scale = self.scale * pinch.scale; + CGFloat newScale = scale; + if (velocity >= 0) + { + newScale += scale * velocity * duration * 0.1; + } + else + { + newScale += scale / (velocity * duration) * 0.1; + } + + if (newScale <= 0 || log2(newScale) < _mbglMap->getMinZoom()) + { + velocity = 0; + } + + if (velocity) + { + CGPoint pinchCenter = [pinch locationInView:pinch.view]; + _mbglMap->setScale(newScale, pinchCenter.x, pinchCenter.y, secondsAsDuration(duration)); + } + _mbglMap->setGestureInProgress(false); - + [self unrotateIfNeededAnimated:YES]; + + if (velocity) + { + self.animatingGesture = YES; - [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)]; + __weak MGLMapView *weakSelf = self; + + [self animateWithDelay:duration animations:^ + { + weakSelf.animatingGesture = NO; + + [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)]; + }]; + } + else + { + [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)]; + } } } @@ -851,11 +890,40 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) } else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled) { - _mbglMap->setGestureInProgress(false); - - [self unrotateIfNeededAnimated:YES]; - - [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)]; + CGFloat velocity = rotate.velocity; + + if (fabs(velocity) > 3) + { + CGFloat radians = self.angle + rotate.rotation; + CGFloat duration = UIScrollViewDecelerationRateNormal; + CGFloat newRadians = radians + velocity * duration * 0.1; + CGFloat newDegrees = [MGLMapView radiansToDegrees:newRadians] * -1; + + _mbglMap->setBearing(newDegrees, secondsAsDuration(duration)); + + _mbglMap->setGestureInProgress(false); + + self.animatingGesture = YES; + + __weak MGLMapView *weakSelf = self; + + [self animateWithDelay:duration animations:^ + { + weakSelf.animatingGesture = NO; + + [weakSelf unrotateIfNeededAnimated:YES]; + + [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)]; + }]; + } + else + { + _mbglMap->setGestureInProgress(false); + + [self unrotateIfNeededAnimated:YES]; + + [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)]; + } } }