From 54ad83e87c4540785b9300b03b0084335f3e89c9 Mon Sep 17 00:00:00 2001 From: paulsUsername <32492218+paulsUsername@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:03:07 +0200 Subject: [PATCH] add gesture handlers --- .../Annotations/CircleAnnotationExample.swift | 50 ++++++++-- .../CustomPointAnnotationExample.swift | 8 ++ .../Testing Examples/PuckPlayground.swift | 5 +- CHANGELOG.md | 1 + .../AnnotationManagerExtensions.swift | 1 + .../Annotations/AnnotationOrchestrator.swift | 9 +- .../Generated/CircleAnnotation.swift | 41 +++++++- .../Generated/CircleAnnotationManager.swift | 78 +++++++++++---- .../Generated/PointAnnotation.swift | 41 +++++++- .../Generated/PointAnnotationManager.swift | 80 +++++++++++---- .../Generated/PolygonAnnotation.swift | 41 +++++++- .../Generated/PolygonAnnotationManager.swift | 78 +++++++++++---- .../Generated/PolylineAnnotation.swift | 41 +++++++- .../Generated/PolylineAnnotationManager.swift | 78 +++++++++++---- .../MapboxMaps/Gestures/GestureManager.swift | 2 +- .../Gestures/MapContentGestureManager.swift | 14 ++- .../CircleAnnotationManagerTests.swift | 90 ++++++++++++++++- .../Generated/CircleAnnotationTests.swift | 7 +- .../PointAnnotationManagerTests.swift | 98 +++++++++++++++++-- .../Generated/PointAnnotationTests.swift | 7 +- .../PolygonAnnotationManagerTests.swift | 97 +++++++++++++++++- .../Generated/PolygonAnnotationTests.swift | 7 +- .../PolylineAnnotationManagerTests.swift | 91 ++++++++++++++++- .../Generated/PolylineAnnotationTests.swift | 7 +- .../Mocks/MockAnnotationManager.swift | 16 +-- .../MapContentGestureManagerTests.swift | 26 +++-- 26 files changed, 869 insertions(+), 145 deletions(-) create mode 100644 Sources/MapboxMaps/Annotations/AnnotationManagerExtensions.swift diff --git a/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift b/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift index 7ad3c27592a8..18d56c49e44f 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift +++ b/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift @@ -14,21 +14,57 @@ final class CircleAnnotationExample: UIViewController, ExampleProtocol { mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) - // Create the CircleAnnotationManager - // Annotation managers are kept alive by `AnnotationOrchestrator` - // (`mapView.annotations`) until you explicitly destroy them - // by calling `mapView.annotations.removeAnnotationManager(withId:)` + /// Create the CircleAnnotationManager + /// Annotation managers are kept alive by `AnnotationOrchestrator` + /// (`mapView.annotations`) until you explicitly destroy them + /// by calling `mapView.annotations.removeAnnotationManager(withId:)` let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager() var annotations = [CircleAnnotation]() for _ in 0...2000 { var annotation = CircleAnnotation(centerCoordinate: .random) annotation.circleColor = StyleColor(.random) + annotation.circleStrokeColor = StyleColor(UIColor.black) annotation.circleRadius = 12 annotation.isDraggable = true - annotation.tapHandler = { [id = annotation.id] _ in - print("tapped annotation \(id)") - return false + annotation.circleStrokeWidth = 0 + + /// The following handlers add tap and longpress gesture handlers. The `context` parameter + /// contains the `point` of the gesture in view coordinate system and a geographical `coordinate`. + annotation.tapHandler = { [id = annotation.id] context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation tap: \(id), \(latlon)") + return true // don't propagate tap to annotations below + } + annotation.longPressHandler = { [id = annotation.id] context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation longpress: \(id), \(latlon)") + return true // don't propagate tap to annotations below + } + + /// The following gesture handlers create the dragging effect. + /// The dragged annotation becomes larger and receives a stroke. + /// + /// - Important: In order to modify the annotation while it is being dragged, + /// use the inout `annotation` that comes as the first argument to the handler. + /// Don't use the source annotation that you used to configure it initially. + /// The annotations are value types. + /// + /// The second `context` argument is similar to tap and longpress gestures. + annotation.dragBeginHandler = { annotation, _ in + annotation.circleRadius = 22 + annotation.circleStrokeWidth = 2 + print("annotation drag begin: \(annotation.id)") + return true // allow drag gesture begin + } + annotation.dragChangeHandler = { annotation, context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation drag: \(annotation.id), \(latlon)") + } + annotation.dragEndHandler = { annotation, _ in + annotation.circleRadius = 12 + annotation.circleStrokeWidth = 0 + print("annotation drag ended: \(annotation.id)") } annotations.append(annotation) } diff --git a/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift b/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift index d62b5332e555..84cf6aaf8698 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift +++ b/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift @@ -51,6 +51,14 @@ final class CustomPointAnnotationExample: UIViewController, ExampleProtocol { return true } + customPointAnnotation.dragBeginHandler = { annotation, _ in + annotation.iconSize = 1.2 + return true // allow drag gesture begin + } + customPointAnnotation.dragEndHandler = { annotation, _ in + annotation.iconSize = 1 + } + // Add the annotation to the manager in order to render it on the map. pointAnnotationManager.annotations = [customPointAnnotation] } diff --git a/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift index 53d91cf1ddfa..541aa2f2dbd0 100644 --- a/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift +++ b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift @@ -41,6 +41,7 @@ struct PuckPlayground: View { } } .mapStyle(mapStyle) + .debugOptions(.padding) .additionalSafeAreaInsets(sidePanel ? .trailing : .bottom, settingsHeight) .ignoresSafeArea() .safeOverlay(alignment: sidePanel ? .trailing : .bottom) { @@ -49,8 +50,8 @@ struct PuckPlayground: View { .onChangeOfSize { settingsHeight = sidePanel ? $0.width : $0.height } } .safeOverlay(alignment: .trailing) { - MapStyleSelectorButton(mapStyle: $mapStyle) - .padding(.trailing, sidePanel ? 300 : 0) + MapStyleSelectorButton(mapStyle: $mapStyle) + .padding(.trailing, sidePanel ? 300 : 0) } .onChange(of: puckType) { newValue in if puckType == .d3 { // Switch to dusk mode to see model light emission diff --git a/CHANGELOG.md b/CHANGELOG.md index 74bb82dc64e2..23e24e750e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Mapbox welcomes participation and contributions from everyone. ## main * Add `onClusterTap` and `onClusterLongPress` to AnnotationManagers(UIKit) and AnnotationGroups(SwiftUI) which support clustering +* Add annotations drag handlers callbacks `dragBeginHandler`, `dragChangeHandler`, `dragEndHandler` to all Annotation types. ## 11.2.0-beta.1 - 1 February, 2024 diff --git a/Sources/MapboxMaps/Annotations/AnnotationManagerExtensions.swift b/Sources/MapboxMaps/Annotations/AnnotationManagerExtensions.swift new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/Sources/MapboxMaps/Annotations/AnnotationManagerExtensions.swift @@ -0,0 +1 @@ + diff --git a/Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift b/Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift index 31944c67de98..a5ef6f132f49 100644 --- a/Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift +++ b/Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift @@ -29,14 +29,17 @@ protocol AnnotationManagerInternal: AnnotationManager { func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool - func handleDragChanged(with translation: CGPoint) + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) - func handleDragEnded() + func handleDragEnd(context: MapContentGestureContext) } -struct AnnotationGestureHandlers { +struct AnnotationGestureHandlers { var tap: ((MapContentGestureContext) -> Bool)? var longPress: ((MapContentGestureContext) -> Bool)? + var dragBegin: ((inout T, MapContentGestureContext) -> Bool)? + var dragChange: ((inout T, MapContentGestureContext) -> Void)? + var dragEnd: ((inout T, MapContentGestureContext) -> Void)? } /// A delegate that is called when a tap is detected on an annotation (or on several of them). diff --git a/Sources/MapboxMaps/Annotations/Generated/CircleAnnotation.swift b/Sources/MapboxMaps/Annotations/Generated/CircleAnnotation.swift index e489acc3cb07..cba249aa24f3 100644 --- a/Sources/MapboxMaps/Annotations/Generated/CircleAnnotation.swift +++ b/Sources/MapboxMaps/Annotations/Generated/CircleAnnotation.swift @@ -37,21 +37,54 @@ public struct CircleAnnotation: Annotation, Equatable { get { gestureHandlers.value.longPress } set { gestureHandlers.value.longPress = newValue } } - + + /// The handler is invoked when the user begins to drag the annotation. + /// + /// The annotation should have `isDraggable` set to `true` to make id draggable. + /// + /// - Note: In SwiftUI, draggable annotations are not supported. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + /// Return `true` to allow dragging to begin, or `false` to prevent it and propagate the gesture to the map's other annotations or layers. + public var dragBeginHandler: ((inout CircleAnnotation, MapContentGestureContext) -> Bool)? { + get { gestureHandlers.value.dragBegin } + set { gestureHandlers.value.dragBegin = newValue } + } + + /// The handler is invoked when annotation is being dragged. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragChangeHandler: ((inout CircleAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragChange } + set { gestureHandlers.value.dragChange = newValue } + } + + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragEndHandler: ((inout CircleAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragEnd } + set { gestureHandlers.value.dragEnd = newValue } + } + /// JSON convertible properties associated with the annotation, used to enrich Feature GeoJSON `properties["custom_data"]` field. public var customData = JSONObject() /// Properties associated with the annotation. /// - /// - Note: This propert doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. + /// - Note: This property doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. @available(*, deprecated, message: "Use customData instead.") public var userInfo: [String: Any]? { get { _userInfo.value } set { _userInfo.value = newValue } } - + private var _userInfo: AlwaysEqual<[String: Any]?> = nil - private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) + private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) var layerProperties: [String: Any] { var properties: [String: Any] = [:] diff --git a/Sources/MapboxMaps/Annotations/Generated/CircleAnnotationManager.swift b/Sources/MapboxMaps/Annotations/Generated/CircleAnnotationManager.swift index ede2f0360eca..2ffff584b26d 100644 --- a/Sources/MapboxMaps/Annotations/Generated/CircleAnnotationManager.swift +++ b/Sources/MapboxMaps/Annotations/Generated/CircleAnnotationManager.swift @@ -10,7 +10,7 @@ public class CircleAnnotationManager: AnnotationManagerInternal { public var sourceId: String { id } public var layerId: String { id } - + private var dragId: String { "\(id)_drag" } public let id: String @@ -282,15 +282,15 @@ public class CircleAnnotationManager: AnnotationManagerInternal { // MARK: - User interaction handling - + func handleTap(layerId: String, feature: Feature, context: MapContentGestureContext) -> Bool { - + guard let featureId = feature.identifier?.string else { return false } - + let tappedIndex = annotations.firstIndex { $0.id == featureId } guard let tappedIndex else { return false } var tappedAnnotation = annotations[tappedIndex] - + tappedAnnotation.isSelected.toggle() if !isSwiftUI { @@ -315,36 +315,61 @@ public class CircleAnnotationManager: AnnotationManagerInternal { func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool { guard !isSwiftUI else { return false } - let predicate = { (annotation: CircleAnnotation) -> Bool in + func predicate(annotation: CircleAnnotation) -> Bool { annotation.id == featureId && annotation.isDraggable } + func tryBeginDragging(_ annotations: inout [CircleAnnotation], idx: Int) -> Bool { + var annotation = annotations[idx] + // If no drag handler set, the dragging is allowed + let dragAllowed = annotation.dragBeginHandler?(&annotation, context) ?? true + annotations[idx] = annotation + return dragAllowed + } + + /// First, try to drag annotations that are already on the dragging layer. if let idx = draggedAnnotations.firstIndex(where: predicate) { + let dragAllowed = tryBeginDragging(&draggedAnnotations, idx: idx) + guard dragAllowed else { + return false + } + draggedAnnotationIndex = idx return true } + /// Then, try to start dragging from the main set of annotations. if let idx = mainAnnotations.lastIndex(where: predicate) { - insertDraggedLayerAndSourceOnce { - let source = GeoJSONSource(id: dragId) - let layer = CircleLayer(id: dragId, source: dragId) - do { - try style.addSource(source) - try style.addPersistentLayer(layer, layerPosition: .above(layerId)) - } catch { - Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") - } + let dragAllowed = tryBeginDragging(&mainAnnotations, idx: idx) + guard dragAllowed else { + return false } + insertDraggedLayerAndSource() + let annotation = mainAnnotations.remove(at: idx) draggedAnnotations.append(annotation) draggedAnnotationIndex = draggedAnnotations.endIndex - 1 return true } + return false } - func handleDragChanged(with translation: CGPoint) { + private func insertDraggedLayerAndSource() { + insertDraggedLayerAndSourceOnce { + let source = GeoJSONSource(id: dragId) + let layer = CircleLayer(id: dragId, source: dragId) + do { + try style.addSource(source) + try style.addPersistentLayer(layer, layerPosition: .above(layerId)) + } catch { + Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") + } + } + } + + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) { guard !isSwiftUI, let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex, @@ -353,12 +378,31 @@ public class CircleAnnotationManager: AnnotationManagerInternal { } draggedAnnotations[draggedAnnotationIndex].point = point + + callDragHandler(\.dragChangeHandler, context: context) } - internal func handleDragEnded() { + func handleDragEnd(context: MapContentGestureContext) { guard !isSwiftUI else { return } + callDragHandler(\.dragEndHandler, context: context) draggedAnnotationIndex = nil } + + private func callDragHandler( + _ keyPath: KeyPath Void)?>, + context: MapContentGestureContext + ) { + guard let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex else { + return + } + + if let handler = draggedAnnotations[draggedAnnotationIndex][keyPath: keyPath] { + var copy = draggedAnnotations[draggedAnnotationIndex] + handler(©, context) + draggedAnnotations[draggedAnnotationIndex] = copy + } + } } + // End of generated file. diff --git a/Sources/MapboxMaps/Annotations/Generated/PointAnnotation.swift b/Sources/MapboxMaps/Annotations/Generated/PointAnnotation.swift index b43a3a8861b6..b6823584104b 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PointAnnotation.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PointAnnotation.swift @@ -37,21 +37,54 @@ public struct PointAnnotation: Annotation, Equatable { get { gestureHandlers.value.longPress } set { gestureHandlers.value.longPress = newValue } } - + + /// The handler is invoked when the user begins to drag the annotation. + /// + /// The annotation should have `isDraggable` set to `true` to make id draggable. + /// + /// - Note: In SwiftUI, draggable annotations are not supported. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + /// Return `true` to allow dragging to begin, or `false` to prevent it and propagate the gesture to the map's other annotations or layers. + public var dragBeginHandler: ((inout PointAnnotation, MapContentGestureContext) -> Bool)? { + get { gestureHandlers.value.dragBegin } + set { gestureHandlers.value.dragBegin = newValue } + } + + /// The handler is invoked when annotation is being dragged. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragChangeHandler: ((inout PointAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragChange } + set { gestureHandlers.value.dragChange = newValue } + } + + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragEndHandler: ((inout PointAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragEnd } + set { gestureHandlers.value.dragEnd = newValue } + } + /// JSON convertible properties associated with the annotation, used to enrich Feature GeoJSON `properties["custom_data"]` field. public var customData = JSONObject() /// Properties associated with the annotation. /// - /// - Note: This propert doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. + /// - Note: This property doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. @available(*, deprecated, message: "Use customData instead.") public var userInfo: [String: Any]? { get { _userInfo.value } set { _userInfo.value = newValue } } - + private var _userInfo: AlwaysEqual<[String: Any]?> = nil - private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) + private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) var layerProperties: [String: Any] { var properties: [String: Any] = [:] diff --git a/Sources/MapboxMaps/Annotations/Generated/PointAnnotationManager.swift b/Sources/MapboxMaps/Annotations/Generated/PointAnnotationManager.swift index f6ce6aa46505..cdb62aa7c1e0 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PointAnnotationManager.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PointAnnotationManager.swift @@ -10,11 +10,11 @@ public class PointAnnotationManager: AnnotationManagerInternal { public var sourceId: String { id } public var layerId: String { id } - + private var dragId: String { "\(id)_drag" } private var clusterId: String { "mapbox-iOS-cluster-circle-layer-manager-\(id)" } - + public let id: String /// The collection of ``PointAnnotation`` being managed. @@ -644,7 +644,7 @@ public class PointAnnotationManager: AnnotationManagerInternal { .getAnnotationClusterContext(layerId: id, feature: feature, context: context, completion: completion) .erased } - + func handleTap(layerId: String, feature: Feature, context: MapContentGestureContext) -> Bool { if layerId == clusterId, let onClusterTap { queryAnnotationClusterContext(feature: feature, context: context) { result in @@ -654,13 +654,13 @@ public class PointAnnotationManager: AnnotationManagerInternal { } return true } - + guard let featureId = feature.identifier?.string else { return false } - + let tappedIndex = annotations.firstIndex { $0.id == featureId } guard let tappedIndex else { return false } var tappedAnnotation = annotations[tappedIndex] - + tappedAnnotation.isSelected.toggle() if !isSwiftUI { @@ -693,36 +693,61 @@ public class PointAnnotationManager: AnnotationManagerInternal { func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool { guard !isSwiftUI else { return false } - let predicate = { (annotation: PointAnnotation) -> Bool in + func predicate(annotation: PointAnnotation) -> Bool { annotation.id == featureId && annotation.isDraggable } + func tryBeginDragging(_ annotations: inout [PointAnnotation], idx: Int) -> Bool { + var annotation = annotations[idx] + // If no drag handler set, the dragging is allowed + let dragAllowed = annotation.dragBeginHandler?(&annotation, context) ?? true + annotations[idx] = annotation + return dragAllowed + } + + /// First, try to drag annotations that are already on the dragging layer. if let idx = draggedAnnotations.firstIndex(where: predicate) { + let dragAllowed = tryBeginDragging(&draggedAnnotations, idx: idx) + guard dragAllowed else { + return false + } + draggedAnnotationIndex = idx return true } + /// Then, try to start dragging from the main set of annotations. if let idx = mainAnnotations.lastIndex(where: predicate) { - insertDraggedLayerAndSourceOnce { - let source = GeoJSONSource(id: dragId) - let layer = SymbolLayer(id: dragId, source: dragId) - do { - try style.addSource(source) - try style.addPersistentLayer(layer, layerPosition: .above(layerId)) - } catch { - Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") - } + let dragAllowed = tryBeginDragging(&mainAnnotations, idx: idx) + guard dragAllowed else { + return false } + insertDraggedLayerAndSource() + let annotation = mainAnnotations.remove(at: idx) draggedAnnotations.append(annotation) draggedAnnotationIndex = draggedAnnotations.endIndex - 1 return true } + return false } - func handleDragChanged(with translation: CGPoint) { + private func insertDraggedLayerAndSource() { + insertDraggedLayerAndSourceOnce { + let source = GeoJSONSource(id: dragId) + let layer = SymbolLayer(id: dragId, source: dragId) + do { + try style.addSource(source) + try style.addPersistentLayer(layer, layerPosition: .above(layerId)) + } catch { + Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") + } + } + } + + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) { guard !isSwiftUI, let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex, @@ -731,12 +756,30 @@ public class PointAnnotationManager: AnnotationManagerInternal { } draggedAnnotations[draggedAnnotationIndex].point = point + + callDragHandler(\.dragChangeHandler, context: context) } - internal func handleDragEnded() { + func handleDragEnd(context: MapContentGestureContext) { guard !isSwiftUI else { return } + callDragHandler(\.dragEndHandler, context: context) draggedAnnotationIndex = nil } + + private func callDragHandler( + _ keyPath: KeyPath Void)?>, + context: MapContentGestureContext + ) { + guard let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex else { + return + } + + if let handler = draggedAnnotations[draggedAnnotationIndex][keyPath: keyPath] { + var copy = draggedAnnotations[draggedAnnotationIndex] + handler(©, context) + draggedAnnotations[draggedAnnotationIndex] = copy + } + } } private extension PointAnnotationManager { @@ -759,4 +802,5 @@ private extension PointAnnotationManager { removeImages(imagesToRemove) } } + // End of generated file. diff --git a/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotation.swift b/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotation.swift index ed370ac42963..3229d2a780b0 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotation.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotation.swift @@ -37,21 +37,54 @@ public struct PolygonAnnotation: Annotation, Equatable { get { gestureHandlers.value.longPress } set { gestureHandlers.value.longPress = newValue } } - + + /// The handler is invoked when the user begins to drag the annotation. + /// + /// The annotation should have `isDraggable` set to `true` to make id draggable. + /// + /// - Note: In SwiftUI, draggable annotations are not supported. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + /// Return `true` to allow dragging to begin, or `false` to prevent it and propagate the gesture to the map's other annotations or layers. + public var dragBeginHandler: ((inout PolygonAnnotation, MapContentGestureContext) -> Bool)? { + get { gestureHandlers.value.dragBegin } + set { gestureHandlers.value.dragBegin = newValue } + } + + /// The handler is invoked when annotation is being dragged. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragChangeHandler: ((inout PolygonAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragChange } + set { gestureHandlers.value.dragChange = newValue } + } + + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragEndHandler: ((inout PolygonAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragEnd } + set { gestureHandlers.value.dragEnd = newValue } + } + /// JSON convertible properties associated with the annotation, used to enrich Feature GeoJSON `properties["custom_data"]` field. public var customData = JSONObject() /// Properties associated with the annotation. /// - /// - Note: This propert doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. + /// - Note: This property doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. @available(*, deprecated, message: "Use customData instead.") public var userInfo: [String: Any]? { get { _userInfo.value } set { _userInfo.value = newValue } } - + private var _userInfo: AlwaysEqual<[String: Any]?> = nil - private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) + private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) var layerProperties: [String: Any] { var properties: [String: Any] = [:] diff --git a/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotationManager.swift b/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotationManager.swift index 8a218cebf8bf..6d2b49eaccb8 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotationManager.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PolygonAnnotationManager.swift @@ -10,7 +10,7 @@ public class PolygonAnnotationManager: AnnotationManagerInternal { public var sourceId: String { id } public var layerId: String { id } - + private var dragId: String { "\(id)_drag" } public let id: String @@ -272,15 +272,15 @@ public class PolygonAnnotationManager: AnnotationManagerInternal { // MARK: - User interaction handling - + func handleTap(layerId: String, feature: Feature, context: MapContentGestureContext) -> Bool { - + guard let featureId = feature.identifier?.string else { return false } - + let tappedIndex = annotations.firstIndex { $0.id == featureId } guard let tappedIndex else { return false } var tappedAnnotation = annotations[tappedIndex] - + tappedAnnotation.isSelected.toggle() if !isSwiftUI { @@ -305,36 +305,61 @@ public class PolygonAnnotationManager: AnnotationManagerInternal { func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool { guard !isSwiftUI else { return false } - let predicate = { (annotation: PolygonAnnotation) -> Bool in + func predicate(annotation: PolygonAnnotation) -> Bool { annotation.id == featureId && annotation.isDraggable } + func tryBeginDragging(_ annotations: inout [PolygonAnnotation], idx: Int) -> Bool { + var annotation = annotations[idx] + // If no drag handler set, the dragging is allowed + let dragAllowed = annotation.dragBeginHandler?(&annotation, context) ?? true + annotations[idx] = annotation + return dragAllowed + } + + /// First, try to drag annotations that are already on the dragging layer. if let idx = draggedAnnotations.firstIndex(where: predicate) { + let dragAllowed = tryBeginDragging(&draggedAnnotations, idx: idx) + guard dragAllowed else { + return false + } + draggedAnnotationIndex = idx return true } + /// Then, try to start dragging from the main set of annotations. if let idx = mainAnnotations.lastIndex(where: predicate) { - insertDraggedLayerAndSourceOnce { - let source = GeoJSONSource(id: dragId) - let layer = FillLayer(id: dragId, source: dragId) - do { - try style.addSource(source) - try style.addPersistentLayer(layer, layerPosition: .above(layerId)) - } catch { - Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") - } + let dragAllowed = tryBeginDragging(&mainAnnotations, idx: idx) + guard dragAllowed else { + return false } + insertDraggedLayerAndSource() + let annotation = mainAnnotations.remove(at: idx) draggedAnnotations.append(annotation) draggedAnnotationIndex = draggedAnnotations.endIndex - 1 return true } + return false } - func handleDragChanged(with translation: CGPoint) { + private func insertDraggedLayerAndSource() { + insertDraggedLayerAndSourceOnce { + let source = GeoJSONSource(id: dragId) + let layer = FillLayer(id: dragId, source: dragId) + do { + try style.addSource(source) + try style.addPersistentLayer(layer, layerPosition: .above(layerId)) + } catch { + Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") + } + } + } + + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) { guard !isSwiftUI, let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex, @@ -343,12 +368,31 @@ public class PolygonAnnotationManager: AnnotationManagerInternal { } draggedAnnotations[draggedAnnotationIndex].polygon = polygon + + callDragHandler(\.dragChangeHandler, context: context) } - internal func handleDragEnded() { + func handleDragEnd(context: MapContentGestureContext) { guard !isSwiftUI else { return } + callDragHandler(\.dragEndHandler, context: context) draggedAnnotationIndex = nil } + + private func callDragHandler( + _ keyPath: KeyPath Void)?>, + context: MapContentGestureContext + ) { + guard let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex else { + return + } + + if let handler = draggedAnnotations[draggedAnnotationIndex][keyPath: keyPath] { + var copy = draggedAnnotations[draggedAnnotationIndex] + handler(©, context) + draggedAnnotations[draggedAnnotationIndex] = copy + } + } } + // End of generated file. diff --git a/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotation.swift b/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotation.swift index e98b4692bceb..b0605b39880c 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotation.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotation.swift @@ -37,21 +37,54 @@ public struct PolylineAnnotation: Annotation, Equatable { get { gestureHandlers.value.longPress } set { gestureHandlers.value.longPress = newValue } } - + + /// The handler is invoked when the user begins to drag the annotation. + /// + /// The annotation should have `isDraggable` set to `true` to make id draggable. + /// + /// - Note: In SwiftUI, draggable annotations are not supported. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + /// Return `true` to allow dragging to begin, or `false` to prevent it and propagate the gesture to the map's other annotations or layers. + public var dragBeginHandler: ((inout PolylineAnnotation, MapContentGestureContext) -> Bool)? { + get { gestureHandlers.value.dragBegin } + set { gestureHandlers.value.dragBegin = newValue } + } + + /// The handler is invoked when annotation is being dragged. + /// + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragChangeHandler: ((inout PolylineAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragChange } + set { gestureHandlers.value.dragChange = newValue } + } + + /// The handler receives the `annotation` and the `context` parameters of the gesture: + /// - Use the `annotation` inout property to update properties of the annotation. + /// - The `context` contains position of the gesture. + public var dragEndHandler: ((inout PolylineAnnotation, MapContentGestureContext) -> Void)? { + get { gestureHandlers.value.dragEnd } + set { gestureHandlers.value.dragEnd = newValue } + } + /// JSON convertible properties associated with the annotation, used to enrich Feature GeoJSON `properties["custom_data"]` field. public var customData = JSONObject() /// Properties associated with the annotation. /// - /// - Note: This propert doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. + /// - Note: This property doesn't participate in `Equatable` comparisions and will strip non-JSON values when encoding to Feature GeoJSON. @available(*, deprecated, message: "Use customData instead.") public var userInfo: [String: Any]? { get { _userInfo.value } set { _userInfo.value = newValue } } - + private var _userInfo: AlwaysEqual<[String: Any]?> = nil - private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) + private var gestureHandlers = AlwaysEqual(value: AnnotationGestureHandlers()) var layerProperties: [String: Any] { var properties: [String: Any] = [:] diff --git a/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotationManager.swift b/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotationManager.swift index d553c72203d9..3b3d6ef06cd3 100644 --- a/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotationManager.swift +++ b/Sources/MapboxMaps/Annotations/Generated/PolylineAnnotationManager.swift @@ -10,7 +10,7 @@ public class PolylineAnnotationManager: AnnotationManagerInternal { public var sourceId: String { id } public var layerId: String { id } - + private var dragId: String { "\(id)_drag" } public let id: String @@ -322,15 +322,15 @@ public class PolylineAnnotationManager: AnnotationManagerInternal { // MARK: - User interaction handling - + func handleTap(layerId: String, feature: Feature, context: MapContentGestureContext) -> Bool { - + guard let featureId = feature.identifier?.string else { return false } - + let tappedIndex = annotations.firstIndex { $0.id == featureId } guard let tappedIndex else { return false } var tappedAnnotation = annotations[tappedIndex] - + tappedAnnotation.isSelected.toggle() if !isSwiftUI { @@ -355,36 +355,61 @@ public class PolylineAnnotationManager: AnnotationManagerInternal { func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool { guard !isSwiftUI else { return false } - let predicate = { (annotation: PolylineAnnotation) -> Bool in + func predicate(annotation: PolylineAnnotation) -> Bool { annotation.id == featureId && annotation.isDraggable } + func tryBeginDragging(_ annotations: inout [PolylineAnnotation], idx: Int) -> Bool { + var annotation = annotations[idx] + // If no drag handler set, the dragging is allowed + let dragAllowed = annotation.dragBeginHandler?(&annotation, context) ?? true + annotations[idx] = annotation + return dragAllowed + } + + /// First, try to drag annotations that are already on the dragging layer. if let idx = draggedAnnotations.firstIndex(where: predicate) { + let dragAllowed = tryBeginDragging(&draggedAnnotations, idx: idx) + guard dragAllowed else { + return false + } + draggedAnnotationIndex = idx return true } + /// Then, try to start dragging from the main set of annotations. if let idx = mainAnnotations.lastIndex(where: predicate) { - insertDraggedLayerAndSourceOnce { - let source = GeoJSONSource(id: dragId) - let layer = LineLayer(id: dragId, source: dragId) - do { - try style.addSource(source) - try style.addPersistentLayer(layer, layerPosition: .above(layerId)) - } catch { - Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") - } + let dragAllowed = tryBeginDragging(&mainAnnotations, idx: idx) + guard dragAllowed else { + return false } + insertDraggedLayerAndSource() + let annotation = mainAnnotations.remove(at: idx) draggedAnnotations.append(annotation) draggedAnnotationIndex = draggedAnnotations.endIndex - 1 return true } + return false } - func handleDragChanged(with translation: CGPoint) { + private func insertDraggedLayerAndSource() { + insertDraggedLayerAndSourceOnce { + let source = GeoJSONSource(id: dragId) + let layer = LineLayer(id: dragId, source: dragId) + do { + try style.addSource(source) + try style.addPersistentLayer(layer, layerPosition: .above(layerId)) + } catch { + Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations") + } + } + } + + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) { guard !isSwiftUI, let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex, @@ -393,12 +418,31 @@ public class PolylineAnnotationManager: AnnotationManagerInternal { } draggedAnnotations[draggedAnnotationIndex].lineString = lineString + + callDragHandler(\.dragChangeHandler, context: context) } - internal func handleDragEnded() { + func handleDragEnd(context: MapContentGestureContext) { guard !isSwiftUI else { return } + callDragHandler(\.dragEndHandler, context: context) draggedAnnotationIndex = nil } + + private func callDragHandler( + _ keyPath: KeyPath Void)?>, + context: MapContentGestureContext + ) { + guard let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex else { + return + } + + if let handler = draggedAnnotations[draggedAnnotationIndex][keyPath: keyPath] { + var copy = draggedAnnotations[draggedAnnotationIndex] + handler(©, context) + draggedAnnotations[draggedAnnotationIndex] = copy + } + } } + // End of generated file. diff --git a/Sources/MapboxMaps/Gestures/GestureManager.swift b/Sources/MapboxMaps/Gestures/GestureManager.swift index 4df6d358d700..944e05879624 100644 --- a/Sources/MapboxMaps/Gestures/GestureManager.swift +++ b/Sources/MapboxMaps/Gestures/GestureManager.swift @@ -123,7 +123,7 @@ public final class GestureManager: GestureHandlerDelegate { /// Adds a long press handler for the layer with `layerId`. /// - /// The handler will be called in the event, starting with the topmost layer and propagating down to each layer under the tap in order. + /// The handler will be called in the event, starting with the topmost layer and propagating down to each layer under the tap in order. public func onLayerLongPress(_ layerId: String, handler: @escaping MapLayerGestureHandler) -> AnyCancelable { mapContentGestureManager.onLayerLongPress(layerId, handler: handler) } diff --git a/Sources/MapboxMaps/Gestures/MapContentGestureManager.swift b/Sources/MapboxMaps/Gestures/MapContentGestureManager.swift index e45791a0fe22..0201e9d54b05 100644 --- a/Sources/MapboxMaps/Gestures/MapContentGestureManager.swift +++ b/Sources/MapboxMaps/Gestures/MapContentGestureManager.swift @@ -88,7 +88,9 @@ final class MapContentGestureManager: MapContentGestureManagerProtocol { for queriedFeature in queriedFeatures { if !self.handle(using: { manager in manager.handleTap }, queriedFeature: queriedFeature, context: context) { - if !self.handle(subscribers: \.layerTapSubscribers, queriedFeature: queriedFeature, context: context) { continue } + if !self.handle(subscribers: \.layerTapSubscribers, queriedFeature: queriedFeature, context: context) { + continue + } } return } @@ -110,7 +112,9 @@ final class MapContentGestureManager: MapContentGestureManagerProtocol { for queriedFeature in queriedFeatures { if !self.handle(using: { manager in manager.handleLongPress }, queriedFeature: queriedFeature, context: context) { - if !self.handle(subscribers: \.layerLongPressSubscribers, queriedFeature: queriedFeature, context: context) { continue } + if !self.handle(subscribers: \.layerLongPressSubscribers, queriedFeature: queriedFeature, context: context) { + continue + } } isLongPressHandled = true } @@ -123,11 +127,11 @@ final class MapContentGestureManager: MapContentGestureManagerProtocol { case .changed: guard let dragState else { return } let translation = dragState.point - point - dragState.manager.handleDragChanged(with: translation) + dragState.manager.handleDragChange(with: translation, context: context) self.dragState?.point = point case .ended, .cancelled: - dragState?.manager.handleDragEnded() + dragState?.manager.handleDragEnd(context: context) dragState = nil default: @@ -138,7 +142,7 @@ final class MapContentGestureManager: MapContentGestureManagerProtocol { private func handeDragBegin(queriedFeatures: [(String, QueriedFeature)], context: MapContentGestureContext) { if let dragState { assertionFailure() - dragState.manager.handleDragEnded() + dragState.manager.handleDragEnd(context: context) } for (layerId, queriedFeature) in queriedFeatures { diff --git a/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationManagerTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationManagerTests.swift index 81b7789e620f..91900cb9f7e3 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationManagerTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationManagerTests.swift @@ -188,6 +188,7 @@ final class CircleAnnotationManagerTests: XCTestCase, AnnotationInteractionDeleg } } + @available(*, deprecated) func testHandleTap() throws { var annotations = [CircleAnnotation]() for _ in 0...5 { @@ -228,14 +229,14 @@ final class CircleAnnotationManagerTests: XCTestCase, AnnotationInteractionDeleg // invalid id delegateAnnotations = nil - var invalidFeature = Feature(geometry: nil) + let invalidFeature = Feature(geometry: nil) handled = manager.handleTap(layerId: "layerId", feature: invalidFeature, context: context) XCTAssertNil(delegateAnnotations) XCTAssertEqual(handled, false) XCTAssertEqual(taps.count, 1) } - + func testInitialCircleEmissiveStrength() { let initialValue = manager.circleEmissiveStrength XCTAssertNil(initialValue) @@ -704,7 +705,6 @@ final class CircleAnnotationManagerTests: XCTestCase, AnnotationInteractionDeleg style.addSourceStub.reset() style.addPersistentLayerStub.reset() - _ = manager.handleDragBegin(with: "circle1", context: .zero) let addSourceParameters = try XCTUnwrap(style.addSourceStub.invocations.last).parameters @@ -729,7 +729,7 @@ final class CircleAnnotationManagerTests: XCTestCase, AnnotationInteractionDeleg mapboxMap.coordinateForPointStub.defaultReturnValue = .random() mapboxMap.cameraState.zoom = 1 - manager.handleDragChanged(with: .random()) + manager.handleDragChange(with: .random(), context: .zero) $displayLink.send() @@ -742,6 +742,88 @@ final class CircleAnnotationManagerTests: XCTestCase, AnnotationInteractionDeleg } } + func testDragHandlers() throws { + struct GestureData { + var annotation: CircleAnnotation + var context: MapContentGestureContext + } + + var annotation = CircleAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) + annotation.isDraggable = true + + let beginDragStub = Stub(defaultReturnValue: false) + let changeDragStub = Stub() + let endDragStub = Stub() + annotation.dragBeginHandler = { annotation, context in + beginDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragChangeHandler = { annotation, context in + changeDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragEndHandler = { annotation, context in + endDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + manager.annotations = [annotation] + + mapboxMap.pointStub.defaultReturnValue = CGPoint(x: 0, y: 0) + mapboxMap.coordinateForPointStub.defaultReturnValue = .random() + mapboxMap.cameraState.zoom = 1 + + var context = MapContentGestureContext(point: CGPoint(x: 0, y: 1), coordinate: .init(latitude: 2, longitude: 3)) + + // test it twice to cover the case when annotation was already on drag layer. + for _ in 0...1 { + beginDragStub.reset() + changeDragStub.reset() + endDragStub.reset() + + // skipped gesture + beginDragStub.defaultReturnValue = false + var res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 1) + XCTAssertEqual(res, false) + var data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + manager.handleDragEnd(context:context) + XCTAssertEqual(changeDragStub.invocations.count, 0) + XCTAssertEqual(endDragStub.invocations.count, 0) + + // handled gesture + context.point.x += 1 + context.coordinate.latitude += 1 + beginDragStub.defaultReturnValue = true + res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 2) + XCTAssertEqual(res, true) + data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + XCTAssertEqual(changeDragStub.invocations.count, 1) + data = try XCTUnwrap(changeDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragEnd(context:context) + XCTAssertEqual(endDragStub.invocations.count, 1) + data = try XCTUnwrap(endDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + } + } + func testDoesNotUpdateDragSourceWhenNoDragged() { let annotation = CircleAnnotation(id: "circle1", centerCoordinate: .random(), isSelected: false, isDraggable: true) manager.annotations = [annotation] diff --git a/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationTests.swift index f1e1b67faa90..b0a0c88a20b5 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/CircleAnnotationTests.swift @@ -116,6 +116,7 @@ final class CircleAnnotationTests: XCTestCase { XCTAssertEqual(circleStrokeWidth, annotation.circleStrokeWidth) } + @available(*, deprecated) func testUserInfo() throws { var annotation = CircleAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) let userInfo = ["foo": "bar"] @@ -125,7 +126,8 @@ final class CircleAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertEqual(actualUserInfo["foo"] as? String, userInfo["foo"]) } - + + @available(*, deprecated) func testUserInfoNilWhenNonJSONObjectPassed() throws { struct NonJSON: Equatable {} var annotation = CircleAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) @@ -135,7 +137,8 @@ final class CircleAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertNil(actualUserInfo["foo"] as? NonJSON) } - + + @available(*, deprecated) func testCustomData() throws { var annotation = CircleAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) let customData: JSONObject = ["foo": .string("bar")] diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationManagerTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationManagerTests.swift index 3f2125e09eff..3d81af9f3c97 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationManagerTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationManagerTests.swift @@ -204,6 +204,7 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega } } + @available(*, deprecated) func testHandleTap() throws { var annotations = [PointAnnotation]() for _ in 0...5 { @@ -244,20 +245,20 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega // invalid id delegateAnnotations = nil - var invalidFeature = Feature(geometry: nil) + let invalidFeature = Feature(geometry: nil) handled = manager.handleTap(layerId: "layerId", feature: invalidFeature, context: context) XCTAssertNil(delegateAnnotations) XCTAssertEqual(handled, false) XCTAssertEqual(taps.count, 1) } - + func testHandleClusterTap() { let onClusterTap = Stub(defaultReturnValue: ()) let context = MapContentGestureContext(point: .init(x: 1, y: 2), coordinate: .init(latitude: 3, longitude: 4)) let annotationContext = AnnotationClusterGestureContext(point: context.point, coordinate: context.coordinate, expansionZoom: 4) manager.onClusterTap = onClusterTap.call - + let isHandled = manager.handleTap( layerId: "mapbox-iOS-cluster-circle-layer-manager-\(id)", feature: annotations[1].feature, @@ -266,15 +267,15 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega mapFeatureQueryable.getGeoJsonClusterExpansionZoomStub.invocations.map(\.parameters.completion).forEach { completion in completion(.success(FeatureExtensionValue(value: 4, features: nil))) } - + XCTAssertTrue(isHandled) XCTAssertEqual(mapFeatureQueryable.getGeoJsonClusterExpansionZoomStub.invocations.map(\.parameters.feature), [ annotations[1].feature ]) - + XCTAssertEqual(onClusterTap.invocations.map(\.parameters), [annotationContext]) } - + func testInitialIconAllowOverlap() { let initialValue = manager.iconAllowOverlap XCTAssertNil(initialValue) @@ -3428,7 +3429,6 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega style.addSourceStub.reset() style.addPersistentLayerStub.reset() - _ = manager.handleDragBegin(with: "point1", context: .zero) let addSourceParameters = try XCTUnwrap(style.addSourceStub.invocations.last).parameters @@ -3453,7 +3453,7 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega mapboxMap.coordinateForPointStub.defaultReturnValue = .random() mapboxMap.cameraState.zoom = 1 - manager.handleDragChanged(with: .random()) + manager.handleDragChange(with: .random(), context: .zero) $displayLink.send() @@ -3466,6 +3466,88 @@ final class PointAnnotationManagerTests: XCTestCase, AnnotationInteractionDelega } } + func testDragHandlers() throws { + struct GestureData { + var annotation: PointAnnotation + var context: MapContentGestureContext + } + + var annotation = PointAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) + annotation.isDraggable = true + + let beginDragStub = Stub(defaultReturnValue: false) + let changeDragStub = Stub() + let endDragStub = Stub() + annotation.dragBeginHandler = { annotation, context in + beginDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragChangeHandler = { annotation, context in + changeDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragEndHandler = { annotation, context in + endDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + manager.annotations = [annotation] + + mapboxMap.pointStub.defaultReturnValue = CGPoint(x: 0, y: 0) + mapboxMap.coordinateForPointStub.defaultReturnValue = .random() + mapboxMap.cameraState.zoom = 1 + + var context = MapContentGestureContext(point: CGPoint(x: 0, y: 1), coordinate: .init(latitude: 2, longitude: 3)) + + // test it twice to cover the case when annotation was already on drag layer. + for _ in 0...1 { + beginDragStub.reset() + changeDragStub.reset() + endDragStub.reset() + + // skipped gesture + beginDragStub.defaultReturnValue = false + var res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 1) + XCTAssertEqual(res, false) + var data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + manager.handleDragEnd(context:context) + XCTAssertEqual(changeDragStub.invocations.count, 0) + XCTAssertEqual(endDragStub.invocations.count, 0) + + // handled gesture + context.point.x += 1 + context.coordinate.latitude += 1 + beginDragStub.defaultReturnValue = true + res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 2) + XCTAssertEqual(res, true) + data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + XCTAssertEqual(changeDragStub.invocations.count, 1) + data = try XCTUnwrap(changeDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragEnd(context:context) + XCTAssertEqual(endDragStub.invocations.count, 1) + data = try XCTUnwrap(endDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + } + } + func testDoesNotUpdateDragSourceWhenNoDragged() { let annotation = PointAnnotation(id: "point1", coordinate: .random(), isSelected: false, isDraggable: true) manager.annotations = [annotation] diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationTests.swift index d2fd48a80f41..f4946c8fdaa1 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PointAnnotationTests.swift @@ -452,6 +452,7 @@ final class PointAnnotationTests: XCTestCase { XCTAssertEqual(textOpacity, annotation.textOpacity) } + @available(*, deprecated) func testUserInfo() throws { var annotation = PointAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) let userInfo = ["foo": "bar"] @@ -461,7 +462,8 @@ final class PointAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertEqual(actualUserInfo["foo"] as? String, userInfo["foo"]) } - + + @available(*, deprecated) func testUserInfoNilWhenNonJSONObjectPassed() throws { struct NonJSON: Equatable {} var annotation = PointAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) @@ -471,7 +473,8 @@ final class PointAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertNil(actualUserInfo["foo"] as? NonJSON) } - + + @available(*, deprecated) func testCustomData() throws { var annotation = PointAnnotation(point: .init(.init(latitude: 0, longitude: 0)), isSelected: false, isDraggable: false) let customData: JSONObject = ["foo": .string("bar")] diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationManagerTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationManagerTests.swift index b91977230a19..5a2b5f232052 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationManagerTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationManagerTests.swift @@ -223,6 +223,7 @@ final class PolygonAnnotationManagerTests: XCTestCase, AnnotationInteractionDele } } + @available(*, deprecated) func testHandleTap() throws { var annotations = [PolygonAnnotation]() for _ in 0...5 { @@ -270,14 +271,14 @@ final class PolygonAnnotationManagerTests: XCTestCase, AnnotationInteractionDele // invalid id delegateAnnotations = nil - var invalidFeature = Feature(geometry: nil) + let invalidFeature = Feature(geometry: nil) handled = manager.handleTap(layerId: "layerId", feature: invalidFeature, context: context) XCTAssertNil(delegateAnnotations) XCTAssertEqual(handled, false) XCTAssertEqual(taps.count, 1) } - + func testInitialFillAntialias() { let initialValue = manager.fillAntialias XCTAssertNil(initialValue) @@ -711,7 +712,6 @@ final class PolygonAnnotationManagerTests: XCTestCase, AnnotationInteractionDele style.addSourceStub.reset() style.addPersistentLayerStub.reset() - _ = manager.handleDragBegin(with: "polygon1", context: .zero) let addSourceParameters = try XCTUnwrap(style.addSourceStub.invocations.last).parameters @@ -736,7 +736,7 @@ final class PolygonAnnotationManagerTests: XCTestCase, AnnotationInteractionDele mapboxMap.coordinateForPointStub.defaultReturnValue = .random() mapboxMap.cameraState.zoom = 1 - manager.handleDragChanged(with: .random()) + manager.handleDragChange(with: .random(), context: .zero) $displayLink.send() @@ -749,6 +749,95 @@ final class PolygonAnnotationManagerTests: XCTestCase, AnnotationInteractionDele } } + func testDragHandlers() throws { + struct GestureData { + var annotation: PolygonAnnotation + var context: MapContentGestureContext + } + + let polygonCoords = [ + CLLocationCoordinate2DMake(24.51713945052515, -89.857177734375), + CLLocationCoordinate2DMake(24.51713945052515, -87.967529296875), + CLLocationCoordinate2DMake(26.244156283890756, -87.967529296875), + CLLocationCoordinate2DMake(26.244156283890756, -89.857177734375), + CLLocationCoordinate2DMake(24.51713945052515, -89.857177734375) + ] + var annotation = PolygonAnnotation(polygon: .init(outerRing: .init(coordinates: polygonCoords)), isSelected: false, isDraggable: false) + annotation.isDraggable = true + + let beginDragStub = Stub(defaultReturnValue: false) + let changeDragStub = Stub() + let endDragStub = Stub() + annotation.dragBeginHandler = { annotation, context in + beginDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragChangeHandler = { annotation, context in + changeDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragEndHandler = { annotation, context in + endDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + manager.annotations = [annotation] + + mapboxMap.pointStub.defaultReturnValue = CGPoint(x: 0, y: 0) + mapboxMap.coordinateForPointStub.defaultReturnValue = .random() + mapboxMap.cameraState.zoom = 1 + + var context = MapContentGestureContext(point: CGPoint(x: 0, y: 1), coordinate: .init(latitude: 2, longitude: 3)) + + // test it twice to cover the case when annotation was already on drag layer. + for _ in 0...1 { + beginDragStub.reset() + changeDragStub.reset() + endDragStub.reset() + + // skipped gesture + beginDragStub.defaultReturnValue = false + var res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 1) + XCTAssertEqual(res, false) + var data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + manager.handleDragEnd(context:context) + XCTAssertEqual(changeDragStub.invocations.count, 0) + XCTAssertEqual(endDragStub.invocations.count, 0) + + // handled gesture + context.point.x += 1 + context.coordinate.latitude += 1 + beginDragStub.defaultReturnValue = true + res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 2) + XCTAssertEqual(res, true) + data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + XCTAssertEqual(changeDragStub.invocations.count, 1) + data = try XCTUnwrap(changeDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragEnd(context:context) + XCTAssertEqual(endDragStub.invocations.count, 1) + data = try XCTUnwrap(endDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + } + } + func testDoesNotUpdateDragSourceWhenNoDragged() { let annotation = PolygonAnnotation(id: "polygon1", polygon: .init([[ CLLocationCoordinate2DMake(24.51713945052515, -89.857177734375), diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationTests.swift index df530d71416b..faa12dbd6876 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PolygonAnnotationTests.swift @@ -109,6 +109,7 @@ final class PolygonAnnotationTests: XCTestCase { XCTAssertEqual(fillPattern, annotation.fillPattern) } + @available(*, deprecated) func testUserInfo() throws { let polygonCoords = [ CLLocationCoordinate2DMake(24.51713945052515, -89.857177734375), @@ -125,7 +126,8 @@ final class PolygonAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertEqual(actualUserInfo["foo"] as? String, userInfo["foo"]) } - + + @available(*, deprecated) func testUserInfoNilWhenNonJSONObjectPassed() throws { struct NonJSON: Equatable {} let polygonCoords = [ @@ -142,7 +144,8 @@ final class PolygonAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertNil(actualUserInfo["foo"] as? NonJSON) } - + + @available(*, deprecated) func testCustomData() throws { let polygonCoords = [ CLLocationCoordinate2DMake(24.51713945052515, -89.857177734375), diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationManagerTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationManagerTests.swift index cafe5c3eb47e..60696394a46a 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationManagerTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationManagerTests.swift @@ -193,6 +193,7 @@ final class PolylineAnnotationManagerTests: XCTestCase, AnnotationInteractionDel } } + @available(*, deprecated) func testHandleTap() throws { var annotations = [PolylineAnnotation]() for _ in 0...5 { @@ -234,14 +235,14 @@ final class PolylineAnnotationManagerTests: XCTestCase, AnnotationInteractionDel // invalid id delegateAnnotations = nil - var invalidFeature = Feature(geometry: nil) + let invalidFeature = Feature(geometry: nil) handled = manager.handleTap(layerId: "layerId", feature: invalidFeature, context: context) XCTAssertNil(delegateAnnotations) XCTAssertEqual(handled, false) XCTAssertEqual(taps.count, 1) } - + func testInitialLineCap() { let initialValue = manager.lineCap XCTAssertNil(initialValue) @@ -1030,7 +1031,6 @@ final class PolylineAnnotationManagerTests: XCTestCase, AnnotationInteractionDel style.addSourceStub.reset() style.addPersistentLayerStub.reset() - _ = manager.handleDragBegin(with: "polyline1", context: .zero) let addSourceParameters = try XCTUnwrap(style.addSourceStub.invocations.last).parameters @@ -1055,7 +1055,7 @@ final class PolylineAnnotationManagerTests: XCTestCase, AnnotationInteractionDel mapboxMap.coordinateForPointStub.defaultReturnValue = .random() mapboxMap.cameraState.zoom = 1 - manager.handleDragChanged(with: .random()) + manager.handleDragChange(with: .random(), context: .zero) $displayLink.send() @@ -1068,6 +1068,89 @@ final class PolylineAnnotationManagerTests: XCTestCase, AnnotationInteractionDel } } + func testDragHandlers() throws { + struct GestureData { + var annotation: PolylineAnnotation + var context: MapContentGestureContext + } + + let lineCoordinates = [ CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10) ] + var annotation = PolylineAnnotation(lineString: .init(lineCoordinates), isSelected: false, isDraggable: false) + annotation.isDraggable = true + + let beginDragStub = Stub(defaultReturnValue: false) + let changeDragStub = Stub() + let endDragStub = Stub() + annotation.dragBeginHandler = { annotation, context in + beginDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragChangeHandler = { annotation, context in + changeDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + annotation.dragEndHandler = { annotation, context in + endDragStub.call(with: GestureData(annotation: annotation, context: context)) + } + manager.annotations = [annotation] + + mapboxMap.pointStub.defaultReturnValue = CGPoint(x: 0, y: 0) + mapboxMap.coordinateForPointStub.defaultReturnValue = .random() + mapboxMap.cameraState.zoom = 1 + + var context = MapContentGestureContext(point: CGPoint(x: 0, y: 1), coordinate: .init(latitude: 2, longitude: 3)) + + // test it twice to cover the case when annotation was already on drag layer. + for _ in 0...1 { + beginDragStub.reset() + changeDragStub.reset() + endDragStub.reset() + + // skipped gesture + beginDragStub.defaultReturnValue = false + var res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 1) + XCTAssertEqual(res, false) + var data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + manager.handleDragEnd(context:context) + XCTAssertEqual(changeDragStub.invocations.count, 0) + XCTAssertEqual(endDragStub.invocations.count, 0) + + // handled gesture + context.point.x += 1 + context.coordinate.latitude += 1 + beginDragStub.defaultReturnValue = true + res = manager.handleDragBegin(with: annotation.id, context: context) + XCTAssertEqual(beginDragStub.invocations.count, 2) + XCTAssertEqual(res, true) + data = try XCTUnwrap(beginDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragChange(with: CGPoint(x: 10, y: 20), context: context) + XCTAssertEqual(changeDragStub.invocations.count, 1) + data = try XCTUnwrap(changeDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + + context.point.x += 1 + context.coordinate.latitude += 1 + manager.handleDragEnd(context:context) + XCTAssertEqual(endDragStub.invocations.count, 1) + data = try XCTUnwrap(endDragStub.invocations.last).parameters + XCTAssertEqual(data.annotation.id, annotation.id) + XCTAssertEqual(data.context.point, context.point) + XCTAssertEqual(data.context.coordinate, context.coordinate) + } + } + func testDoesNotUpdateDragSourceWhenNoDragged() { let annotation = PolylineAnnotation(id: "polyline1", lineCoordinates: [ CLLocationCoordinate2D(latitude: 0, longitude: 0), CLLocationCoordinate2D(latitude: 10, longitude: 10)], isSelected: false, isDraggable: true) manager.annotations = [annotation] diff --git a/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationTests.swift b/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationTests.swift index 896552ace1f0..97132e6713a6 100644 --- a/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationTests.swift +++ b/Tests/MapboxMapsTests/Annotations/Generated/PolylineAnnotationTests.swift @@ -169,6 +169,7 @@ final class PolylineAnnotationTests: XCTestCase { XCTAssertEqual(lineWidth, annotation.lineWidth) } + @available(*, deprecated) func testUserInfo() throws { let lineCoordinates = [ CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10) ] var annotation = PolylineAnnotation(lineString: .init(lineCoordinates), isSelected: false, isDraggable: false) @@ -179,7 +180,8 @@ final class PolylineAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertEqual(actualUserInfo["foo"] as? String, userInfo["foo"]) } - + + @available(*, deprecated) func testUserInfoNilWhenNonJSONObjectPassed() throws { struct NonJSON: Equatable {} let lineCoordinates = [ CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10) ] @@ -190,7 +192,8 @@ final class PolylineAnnotationTests: XCTestCase { let actualUserInfo = try XCTUnwrap(featureProperties["userInfo"]??.rawValue as? [String: Any]) XCTAssertNil(actualUserInfo["foo"] as? NonJSON) } - + + @available(*, deprecated) func testCustomData() throws { let lineCoordinates = [ CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10) ] var annotation = PolylineAnnotation(lineString: .init(lineCoordinates), isSelected: false, isDraggable: false) diff --git a/Tests/MapboxMapsTests/Annotations/Mocks/MockAnnotationManager.swift b/Tests/MapboxMapsTests/Annotations/Mocks/MockAnnotationManager.swift index 6ba6e9afb47a..18d13ccf51f5 100644 --- a/Tests/MapboxMapsTests/Annotations/Mocks/MockAnnotationManager.swift +++ b/Tests/MapboxMapsTests/Annotations/Mocks/MockAnnotationManager.swift @@ -44,13 +44,17 @@ internal final class MockAnnotationManager: AnnotationManagerInternal { handleDragBeginStub(with: DragGestureParams(featureId: featureId, context: context)) } - let handleDragChangedStub = Stub() - func handleDragChanged(with translation: CGPoint) { - handleDragChangedStub(with: translation) + struct DragParams { + var translation: CGPoint + var context: MapContentGestureContext + } + let handleDragChangeStub = Stub() + func handleDragChange(with translation: CGPoint, context: MapContentGestureContext) { + handleDragChangeStub(with: DragParams(translation: translation, context: context)) } - let handleDragEndedStub = Stub() - func handleDragEnded() { - handleDragEndedStub() + let handleDragEndStub = Stub() + func handleDragEnd(context: MapContentGestureContext) { + handleDragEndStub.call(with: context) } } diff --git a/Tests/MapboxMapsTests/Gestures/MapContentGestureManagerTests.swift b/Tests/MapboxMapsTests/Gestures/MapContentGestureManagerTests.swift index 74bbafd0f98b..ef50937b9399 100644 --- a/Tests/MapboxMapsTests/Gestures/MapContentGestureManagerTests.swift +++ b/Tests/MapboxMapsTests/Gestures/MapContentGestureManagerTests.swift @@ -243,7 +243,7 @@ class MapContentGestureManagerTests: XCTestCase { manager.handleDragBeginStub.defaultReturnValue = true // handles the drag var point = CGPoint(x: 10, y: 20) - let coordinate = CLLocationCoordinate2D(latitude: 30, longitude: 40) + var coordinate = CLLocationCoordinate2D(latitude: 30, longitude: 40) mapboxMap.coordinateForPointStub.defaultReturnValue = coordinate $onLongPress.send((point, .began)) @@ -264,20 +264,30 @@ class MapContentGestureManagerTests: XCTestCase { var translation = CGPoint(x: 10, y: 10) point = point + translation + coordinate.latitude += 10 + coordinate.latitude += 5 + mapboxMap.coordinateForPointStub.defaultReturnValue = coordinate $onLongPress.send((point, .changed)) - XCTAssertEqual(manager.handleDragChangedStub.invocations.count, 1) - var dragParams = try XCTUnwrap(manager.handleDragChangedStub.invocations.last).parameters - XCTAssertEqual(dragParams, CGPoint(x: -10, y: -10)) + XCTAssertEqual(manager.handleDragChangeStub.invocations.count, 1) + var dragParams = try XCTUnwrap(manager.handleDragChangeStub.invocations.last).parameters + XCTAssertEqual(dragParams.translation, CGPoint(x: -10, y: -10)) + XCTAssertEqual(dragParams.context.point, CGPoint(x: 20.0, y: 30.0)) + XCTAssertEqual(dragParams.context.coordinate, coordinate) translation = CGPoint(x: 5, y: 2) point = point + translation $onLongPress.send((point, .changed)) - XCTAssertEqual(manager.handleDragChangedStub.invocations.count, 2) - dragParams = try XCTUnwrap(manager.handleDragChangedStub.invocations.last).parameters - XCTAssertEqual(dragParams, CGPoint(x: -5, y: -2)) + XCTAssertEqual(manager.handleDragChangeStub.invocations.count, 2) + dragParams = try XCTUnwrap(manager.handleDragChangeStub.invocations.last).parameters + XCTAssertEqual(dragParams.translation, CGPoint(x: -5, y: -2)) + XCTAssertEqual(dragParams.context.point, CGPoint(x: 25, y: 32)) + XCTAssertEqual(dragParams.context.coordinate, coordinate) $onLongPress.send((point, .ended)) - XCTAssertEqual(manager.handleDragEndedStub.invocations.count, 1) + XCTAssertEqual(manager.handleDragEndStub.invocations.count, 1) + let endContext = try XCTUnwrap(manager.handleDragEndStub.invocations.first).parameters + XCTAssertEqual(endContext.point, CGPoint(x: 25, y: 32)) + XCTAssertEqual(endContext.coordinate, coordinate) } }