Skip to content

Commit

Permalink
Work around pan deceleration on pitched maps (#773)
Browse files Browse the repository at this point in the history
  • Loading branch information
macdrevx authored Oct 20, 2021
1 parent a8fc21b commit 31acae8
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone.
* Set `MapboxMap` flags during gestures and animations. ([#754](https://github.com/mapbox/mapbox-maps-ios/pull/754))
* Treat anchor as constant for `ease(to:)` animations. ([#772](https://github.com/mapbox/mapbox-maps-ios/pull/772))
* Fix experimental snapshot API for iOS 15. ([#760](https://github.com/mapbox/mapbox-maps-ios/pull/760))
* Decelerate more quickly (or not at all) on pitched maps. ([#773](https://github.com/mapbox/mapbox-maps-ios/pull/773))

## 10.0.1 - October 15, 2021

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,21 @@ internal final class PanGestureHandler: GestureHandler, PanGestureHandlerProtoco
// it without further dragging. This specific time interval
// is just the result of manual tuning.
let decelerationTimeout: TimeInterval = 1.0 / 30.0
let maxPitchForDeceleration: CGFloat = 60
guard let initialTouchLocation = initialTouchLocation,
let initialCameraState = initialCameraState,
let lastChangedDate = lastChangedDate,
dateProvider.now.timeIntervalSince(lastChangedDate) < decelerationTimeout else {
dateProvider.now.timeIntervalSince(lastChangedDate) < decelerationTimeout,
mapboxMap.cameraState.pitch <= maxPitchForDeceleration else {
delegate?.gestureEnded(for: .pan, willAnimate: false)
return
}
cameraAnimationsManager.decelerate(
location: touchLocation,
velocity: gestureRecognizer.velocity(in: view),
decelerationFactor: decelerationFactor,
// Decelerate more quickly when pitched. This is a workaround for an issue
// in the drag API that we hope to fix in MapboxCoreMaps in a future release.
decelerationFactor: decelerationFactor * (1 - mapboxMap.cameraState.pitch / 5000),
locationChangeHandler: { (touchLocation) in
// here we capture the initial state so that we can clear
// it immediately after starting the animation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ final class PanGestureHandlerTests: XCTestCase {
clampedTouchLocation: clampedTouchLocation)
}

func testHandlePanChangedWithHorizontalPanScrollingMode() throws {
func testHandlePanChangedWithHorizontalPanMode() throws {
let initialTouchLocation = CGPoint.random()
let changedTouchLocation = CGPoint.random()
let clampedTouchLocation = CGPoint(
Expand All @@ -111,7 +111,7 @@ final class PanGestureHandlerTests: XCTestCase {
clampedTouchLocation: clampedTouchLocation)
}

func testHandlePanChangedWithVerticalPanScrollingMode() throws {
func testHandlePanChangedWithVerticalPanMode() throws {
let initialTouchLocation = CGPoint.random()
let changedTouchLocation = CGPoint.random()
let clampedTouchLocation = CGPoint(
Expand Down Expand Up @@ -143,7 +143,8 @@ final class PanGestureHandlerTests: XCTestCase {
line: UInt = #line) throws {
panGestureHandler.panMode = panMode
panGestureHandler.decelerationFactor = .random(in: 0.1...0.99)
let initialCameraState = CameraState.random()
var initialCameraState = CameraState.random()
initialCameraState.pitch = initialCameraState.pitch.clamped(to: 0...60)
mapboxMap.cameraState = initialCameraState
let endedTouchLocation = CGPoint.random()
gestureRecognizer.locationStub.returnValueQueue = [
Expand Down Expand Up @@ -172,7 +173,7 @@ final class PanGestureHandlerTests: XCTestCase {
let decelerateParams = cameraAnimationsManager.decelerateStub.parameters.first
XCTAssertEqual(decelerateParams?.location, endedTouchLocation, line: line)
XCTAssertEqual(decelerateParams?.velocity, velocity, line: line)
XCTAssertEqual(decelerateParams?.decelerationFactor, panGestureHandler.decelerationFactor, line: line)
XCTAssertEqual(decelerateParams?.decelerationFactor, panGestureHandler.decelerationFactor * (1 - initialCameraState.pitch / 5000), line: line)
let locationChangeHandler = try XCTUnwrap(decelerateParams?.locationChangeHandler)
locationChangeHandler(interpolatedTouchLocation)
XCTAssertEqual(mapboxMap.dragStartStub.parameters, [initialTouchLocation], line: line)
Expand Down Expand Up @@ -202,7 +203,7 @@ final class PanGestureHandlerTests: XCTestCase {
clampedTouchLocation: clampedTouchLocation)
}

func testHandlePanEndedWithHorizontalPanScrollingMode() throws {
func testHandlePanEndedWithHorizontalPanMode() throws {
let initialTouchLocation = CGPoint.random()
let interpolatedTouchLocation = CGPoint.random()
let clampedTouchLocation = CGPoint(
Expand All @@ -216,7 +217,7 @@ final class PanGestureHandlerTests: XCTestCase {
clampedTouchLocation: clampedTouchLocation)
}

func testHandlePanEndedWithVerticalPanScrollingMode() throws {
func testHandlePanEndedWithVerticalPanMode() throws {
let initialTouchLocation = CGPoint.random()
let interpolatedTouchLocation = CGPoint.random()
let clampedTouchLocation = CGPoint(
Expand All @@ -230,6 +231,23 @@ final class PanGestureHandlerTests: XCTestCase {
clampedTouchLocation: clampedTouchLocation)
}

func testHandlePanEndedWithPitchGreaterThan60DoesNotDecelerate() throws {
mapboxMap.cameraState = .random()
mapboxMap.cameraState.pitch = 61
gestureRecognizer.getStateStub.defaultReturnValue = .began
gestureRecognizer.sendActions()
gestureRecognizer.getStateStub.defaultReturnValue = .changed
gestureRecognizer.sendActions()

gestureRecognizer.getStateStub.defaultReturnValue = .ended
gestureRecognizer.sendActions()

XCTAssertEqual(delegate.gestureEndedStub.invocations.count, 1)
XCTAssertEqual(delegate.gestureEndedStub.parameters.first?.gestureType, .pan)
let willAnimate = try XCTUnwrap(delegate.gestureEndedStub.parameters.first?.willAnimate)
XCTAssertFalse(willAnimate)
}

func testHandlePanCancelledDoesNotTriggerDecelerationAnimation() throws {
gestureRecognizer.getStateStub.defaultReturnValue = .cancelled

Expand Down

0 comments on commit 31acae8

Please sign in to comment.