Skip to content

Commit

Permalink
Refactor gestures (#677)
Browse files Browse the repository at this point in the history
#### Breaking Changes

- Pan deceleration has been temporarily removed
- `TapGestureHandler.init` was previously public by mistake and is now internal
- The behavior of `GestureManager.options` has been updated to better reflect the `isEnabled` state of the associated gesture recognizers
- The gesture recognizer properties of `GestureManager` are no longer `Optional`
- `GestureType` has been redesigned so that its cases have a 1-1 relationship with the built-in gestures

#### Public API Additions

- `CameraState`'s fields are now `var`s instead of `let`s for testing purposes, and a public, memberwise initializer has been added.
- `PanScrollingMode` now conforms to `CaseIterable`
- `GestureType` now conforms to `CaseIterable`

#### Bug fixes

- GestureManager no longer sets itself as the delegate of all gestures in MapView when its options change

#### Internal Refactoring

- Generalizes `CameraAnimatorMapboxMap` by renaming it to `MapboxMapProtocol` so that it can be used throughout the SDK. SDK seems small enough that per-component dependency inversion feels unnecessary and might negatively impact binary size.
- Removes old `GestureHandlerDelegate` by injecting `MapboxMap` and `CameraAnimationsManager` into each gesture handler to allow each handler to manipulate the camera directly.
- Removes unnecessary `GestureContextProvider`
- Refactors handlers and `GestureManager` for dependency injection and more thorough tests
- Tidies up handlers and `GestureManager` and associated tests for greater consistency
  • Loading branch information
macdrevx committed Sep 16, 2021
1 parent ac14aae commit 0d393f0
Show file tree
Hide file tree
Showing 57 changed files with 1,856 additions and 1,839 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,22 @@ Mapbox welcomes participation and contributions from everyone.
* `GestureOptions.hapticFeedbackEnabled` has been removed. ([#663](https://github.com/mapbox/mapbox-maps-ios/pull/663))
* `GestureManager.decelarationRate` has been removed and `GestureOptions.decelerationRate` is the single source of truth. ([#662](https://github.com/mapbox/mapbox-maps-ios/pull/662))
* `GestureManager` no longer conforms to `NSObject` and is not a `UIGestureRecognizerDelegate`. ([#669](https://github.com/mapbox/mapbox-maps-ios/pull/669))
* Pan deceleration has been temporarily removed. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* `TapGestureHandler.init` was previously public by mistake and is now internal. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* The behavior of `GestureManager.options` has been updated to better reflect the `isEnabled` state of the associated gesture recognizers. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* The gesture recognizer properties of `GestureManager` are no longer `Optional`. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* `GestureType` has been redesigned so that its cases have a 1-1 relationship with the built-in gestures. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))

### Features ✨ and improvements 🏁

* Allow users to set the map's `MapDebugOptions`. ([#648](https://github.com/mapbox/mapbox-maps-ios/pull/648))
* `CameraState`'s fields are now `var`s instead of `let`s for testing purposes, and a public, memberwise initializer has been added. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* `PanScrollingMode` now conforms to `CaseIterable`. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))
* `GestureType` now conforms to `CaseIterable`. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))

### Bug fixes 🐞

* GestureManager no longer sets itself as the delegate of all gestures in MapView when its options change. ([#677](https://github.com/mapbox/mapbox-maps-ios/pull/677))

## 10.0.0-rc.8 - Sept 8, 2021

Expand Down Expand Up @@ -49,7 +61,6 @@ Mapbox welcomes participation and contributions from everyone.
* Location puck can now hide the accuracy ring. The default value is to hide the accuracy ring. In order to enable the ring, set the `showAccuracyRing` property in `Puck2DConfiguration` to `true`. [#629](https://github.com/mapbox/mapbox-maps-ios/pull/629)
* Annotation interaction delegates are only called when at least one annotation is detected to have been tapped ([638](https://github.com/mapbox/mapbox-maps-ios/issues/638))


### Bug fixes 🐞

* Fix volatile tiles disappearing on "not modified" response ([#628](https://github.com/mapbox/mapbox-maps-ios/pull/628))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class BasicCameraAnimator: NSObject, CameraAnimator, CameraAnimatorInterf
/// The `CameraView` owned by this animator
private let cameraView: CameraView

private let mapboxMap: CameraAnimatorMapboxMap
private let mapboxMap: MapboxMapProtocol

/// Represents the animation that this animator is attempting to execute
private var animation: ((inout CameraTransition) -> Void)?
Expand Down Expand Up @@ -67,7 +67,7 @@ public class BasicCameraAnimator: NSObject, CameraAnimator, CameraAnimatorInterf
// MARK: Initializer
internal init(propertyAnimator: UIViewPropertyAnimator,
owner: AnimationOwner,
mapboxMap: CameraAnimatorMapboxMap,
mapboxMap: MapboxMapProtocol,
cameraView: CameraView) {
self.propertyAnimator = propertyAnimator
self.owner = owner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ internal protocol CameraAnimatorInterface: CameraAnimator {
func update()
}

internal protocol CameraAnimationsManagerProtocol: AnyObject {
func ease(to camera: CameraOptions,
duration: TimeInterval,
curve: UIView.AnimationCurve,
completion: AnimationCompletion?) -> Cancelable?

func cancelAnimations()
}

/// An object that manages a camera's view lifecycle.
public class CameraAnimationsManager {
public class CameraAnimationsManager: CameraAnimationsManagerProtocol {

/// Used to set up camera specific configuration
public var options: CameraBoundsOptions {
Expand Down

This file was deleted.

32 changes: 22 additions & 10 deletions Sources/MapboxMaps/Foundation/Camera/CameraState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@ import CoreLocation
import UIKit

public struct CameraState: Hashable {
public let center: CLLocationCoordinate2D
public let padding: UIEdgeInsets
public let zoom: CGFloat
public let bearing: CLLocationDirection
public let pitch: CGFloat
public var center: CLLocationCoordinate2D
public var padding: UIEdgeInsets
public var zoom: CGFloat
public var bearing: CLLocationDirection
public var pitch: CGFloat

public init(center: CLLocationCoordinate2D,
padding: UIEdgeInsets,
zoom: CGFloat,
bearing: CLLocationDirection,
pitch: CGFloat) {
self.center = center
self.padding = padding
self.zoom = zoom
self.bearing = bearing
self.pitch = pitch
}

internal init(_ objcValue: MapboxCoreMaps.CameraState) {
self.center = objcValue.center
self.padding = objcValue.padding.toUIEdgeInsetsValue()
self.zoom = CGFloat(objcValue.zoom)
self.bearing = CLLocationDirection(objcValue.bearing)
self.pitch = CGFloat(objcValue.pitch)
self.center = objcValue.center
self.padding = objcValue.padding.toUIEdgeInsetsValue()
self.zoom = CGFloat(objcValue.zoom)
self.bearing = CLLocationDirection(objcValue.bearing)
self.pitch = CGFloat(objcValue.pitch)
}

public static func == (lhs: CameraState, rhs: CameraState) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import UIKit

public class FlyToCameraAnimator: NSObject, CameraAnimator, CameraAnimatorInterface {

private let mapboxMap: CameraAnimatorMapboxMap
private let mapboxMap: MapboxMapProtocol

public private(set) var owner: AnimationOwner

Expand All @@ -26,7 +26,7 @@ public class FlyToCameraAnimator: NSObject, CameraAnimator, CameraAnimatorInterf
owner: AnimationOwner,
duration: TimeInterval? = nil,
mapSize: CGSize,
mapboxMap: CameraAnimatorMapboxMap,
mapboxMap: MapboxMapProtocol,
dateProvider: DateProvider = DefaultDateProvider()) {
guard let flyToInterpolator = FlyToInterpolator(from: initial, to: final, cameraBounds: cameraBounds, size: mapSize) else {
return nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapboxMaps/Foundation/MapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ open class MapView: UIView {
mapboxMap: mapboxMap)

// Initialize/Configure gesture manager
gestures = GestureManager(view: self, cameraAnimationsManager: camera, mapboxMap: mapboxMap)
gestures = dependencyProvider.makeGestureManager(view: self, mapboxMap: mapboxMap, cameraAnimationsManager: camera)

// Initialize the attribution manager
attributionDialogManager = AttributionDialogManager(dataSource: mapboxMap, delegate: self)
Expand Down
115 changes: 114 additions & 1 deletion Sources/MapboxMaps/Foundation/MapViewDependencyProvider.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import MetalKit

internal protocol MapViewDependencyProviderProtocol {
internal protocol MapViewDependencyProviderProtocol: AnyObject {
func makeMetalView(frame: CGRect, device: MTLDevice?) -> MTKView
func makeDisplayLink(window: UIWindow, target: Any, selector: Selector) -> DisplayLinkProtocol?
func makeGestureManager(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureManager
}

internal final class MapViewDependencyProvider: MapViewDependencyProviderProtocol {
Expand All @@ -13,4 +16,114 @@ internal final class MapViewDependencyProvider: MapViewDependencyProviderProtoco
func makeDisplayLink(window: UIWindow, target: Any, selector: Selector) -> DisplayLinkProtocol? {
window.screen.displayLink(withTarget: target, selector: selector)
}

func makePanGestureHandler(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UIPanGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return PanGestureHandler(
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makePinchGestureHandler(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UIPinchGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return PinchGestureHandler(
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makeRotationGestureHandler(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UIRotationGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return RotateGestureHandler(
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makePitchGestureHandler(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UIPanGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return PitchGestureHandler(
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makeDoubleTapToZoomGestureHandler(numberOfTouchesRequired: Int,
zoomDelta: CGFloat,
view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UITapGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return DoubleTapToZoomGestureHandler(
numberOfTouchesRequired: numberOfTouchesRequired,
zoomDelta: zoomDelta,
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makeQuickZoomGestureHandler(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureHandler {
let gestureRecognizer = UILongPressGestureRecognizer()
view.addGestureRecognizer(gestureRecognizer)
return QuickZoomGestureHandler(
gestureRecognizer: gestureRecognizer,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager)
}

func makeGestureManager(view: UIView,
mapboxMap: MapboxMapProtocol,
cameraAnimationsManager: CameraAnimationsManagerProtocol) -> GestureManager {
return GestureManager(
decelerationRate: UIScrollView.DecelerationRate.normal.rawValue,
panScrollingMode: .horizontalAndVertical,
panGestureHandler: makePanGestureHandler(
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
pinchGestureHandler: makePinchGestureHandler(
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
rotationGestureHandler: makeRotationGestureHandler(
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
pitchGestureHandler: makePitchGestureHandler(
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
doubleTapToZoomInGestureHandler: makeDoubleTapToZoomGestureHandler(
numberOfTouchesRequired: 1,
zoomDelta: 1,
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
doubleTapToZoomOutGestureHandler: makeDoubleTapToZoomGestureHandler(
numberOfTouchesRequired: 2,
zoomDelta: -1,
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager),
quickZoomGestureHandler: makeQuickZoomGestureHandler(
view: view,
mapboxMap: mapboxMap,
cameraAnimationsManager: cameraAnimationsManager))
}
}
12 changes: 11 additions & 1 deletion Sources/MapboxMaps/Foundation/MapboxMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ import UIKit
@_implementationOnly import MapboxCommon_Private
@_implementationOnly import MapboxCoreMaps_Private

public final class MapboxMap {
internal protocol MapboxMapProtocol: AnyObject {
var cameraBounds: CameraBounds { get }
var cameraState: CameraState { get }
var anchor: CGPoint { get }
func setCamera(to cameraOptions: CameraOptions)
func dragStart(for point: CGPoint)
func dragCameraOptions(from: CGPoint, to: CGPoint) -> CameraOptions
func dragEnd()
}

public final class MapboxMap: MapboxMapProtocol {
/// The underlying renderer object responsible for rendering the map
private let __map: Map

Expand Down
13 changes: 0 additions & 13 deletions Sources/MapboxMaps/Gestures/CameraAnimationsManagerProtocol.swift

This file was deleted.

64 changes: 0 additions & 64 deletions Sources/MapboxMaps/Gestures/GestureHandlerDelegate.swift

This file was deleted.

Loading

0 comments on commit 0d393f0

Please sign in to comment.