From 2bc63f76a4c3200c61902e7133898d7ba35fedc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 17 Apr 2015 12:31:49 -0700 Subject: [PATCH 1/7] Made pan gestures slipperier Replaced the complex formula for overpanning with a simple one that makes the map feel much slipperier than before. `MGLMapView` now overpans just a little more aggressively than `MKMapView` but still a lot less aggressively than Google Maps. Fixes #1266. --- platform/ios/MGLMapView.mm | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 5acf04ef7..b73f2647d 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -677,22 +677,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) { CGPoint velocity = [pan velocityInView:pan.view]; - CGFloat duration = 0; - - 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 / 2, velocity.y * duration / 2); - + CGFloat duration = UIScrollViewDecelerationRateNormal; + CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4); mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); mbglMap->setGestureInProgress(false); From 4c6987b20ea21f6bea096218f871d55dc3497ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 18 Apr 2015 19:40:26 -0700 Subject: [PATCH 2/7] Apply some friction to pan drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the panning velocity is low enough, ignore the pan gesture to simulate friction on the device’s glass screen. --- platform/ios/MGLMapView.mm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index b73f2647d..0586f17ae 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -677,13 +677,22 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) { CGPoint velocity = [pan velocityInView:pan.view]; + if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) < 100) + { + // Not enough velocity to overcome friction + velocity = CGPointZero; + } + CGFloat duration = UIScrollViewDecelerationRateNormal; - CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4); - mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); + if ( ! CGPointEqualToPoint(velocity, CGPointZero)) + { + CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4); + mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); + } mbglMap->setGestureInProgress(false); - if (duration) + if ( ! CGPointEqualToPoint(velocity, CGPointZero)) { self.animatingGesture = YES; From 41d28e7203ab9545fcaaedeeb5836837a865b364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 18 Apr 2015 19:47:52 -0700 Subject: [PATCH 3/7] Added drift-zooming effect after pinch gesture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply a subtle animated zoom after a pinch gesture completes. Lots of magic numbers involved. Avoid drifting when the pinch is near-static, and when the scale factor would go below the minimum. There is more resistance to pinching outward than pinching inward, because it’s far easier to lose your place when you accidentally zoom out. Disabled two-finger pan, which is typically reserved for changing pitch; otherwise, it’s easy to accidentally pan while intending to pinch. --- platform/ios/MGLMapView.mm | 54 ++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 0586f17ae..6c5f3cc2e 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -327,6 +327,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; // _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; _pan.delegate = self; + _pan.maximumNumberOfTouches = 1; [self addGestureRecognizer:_pan]; _scrollEnabled = YES; @@ -747,17 +748,60 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; 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)]; + } } } From 33aeecef220af5ee78551de56a2573d6b1145fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 18 Apr 2015 20:50:35 -0700 Subject: [PATCH 4/7] Added drift-rotation effect after rotation gesture --- platform/ios/MGLMapView.mm | 39 +++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 6c5f3cc2e..330ce289a 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -839,11 +839,40 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; } 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) > 5) + { + CGFloat radians = self.angle + rotate.rotation; + CGFloat duration = UIScrollViewDecelerationRateNormal; + CGFloat newRadians = radians + velocity * duration * 0.1; + CGFloat newDegrees = fmodf([MGLMapView radiansToDegrees:newRadians], 360) * -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)]; + } } } From fae0c4c8b3fb743f4e765a89068861cea49588c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 18 Apr 2015 22:02:15 -0700 Subject: [PATCH 5/7] Simplified math for drift-rotation fmodf() would conflict with the fix for #1199. --- platform/ios/MGLMapView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 330ce289a..78ad046d7 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -846,7 +846,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; CGFloat radians = self.angle + rotate.rotation; CGFloat duration = UIScrollViewDecelerationRateNormal; CGFloat newRadians = radians + velocity * duration * 0.1; - CGFloat newDegrees = fmodf([MGLMapView radiansToDegrees:newRadians], 360) * -1; + CGFloat newDegrees = [MGLMapView radiansToDegrees:newRadians] * -1; mbglMap->setBearing(newDegrees, secondsAsDuration(duration)); From 0aadbedecb24f4c01ea24fd75c9fb7d17b0b8216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 20 Apr 2015 11:15:29 -0700 Subject: [PATCH 6/7] Lower threshold for rotation drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s pretty darn hard to get up to 5 rad/s with the same two fingers. --- platform/ios/MGLMapView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 78ad046d7..274e70208 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -841,7 +841,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; { CGFloat velocity = rotate.velocity; - if (fabs(velocity) > 5) + if (fabs(velocity) > 3) { CGFloat radians = self.angle + rotate.rotation; CGFloat duration = UIScrollViewDecelerationRateNormal; From 5e6bf191446e2edb8c051b2e0f2331825460564f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 1 May 2015 00:37:51 -0400 Subject: [PATCH 7/7] Fixed build errors from merge Follow-up to cf33edf16d60a8967976a8b0296135a106e22ce1. --- platform/ios/MGLMapView.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index db83020cb..99fc55e7e 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -739,7 +739,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) if ( ! CGPointEqualToPoint(velocity, CGPointZero)) { CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4); - mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); + _mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration)); } _mbglMap->setGestureInProgress(false); @@ -821,7 +821,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) newScale += scale / (velocity * duration) * 0.1; } - if (newScale <= 0 || log2(newScale) < mbglMap->getMinZoom()) + if (newScale <= 0 || log2(newScale) < _mbglMap->getMinZoom()) { velocity = 0; }