Skip to content

Commit

Permalink
Refactor Annotations so that the gesture recognizer is managed by the…
Browse files Browse the repository at this point in the history
… AnnotationOrchestrator (#840)

* Check that tap state is began in *AnnotationManager.handleTap(:)

* Move handle tap/QRF to Annotation Orchestrator

* Address feedback

* Generated *AnnotationManager files

* Update Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift

Co-authored-by: Andrew Hershberger <andrew.hershberger@mapbox.com>

* Address feedback, regenerate AnnotationManagers

* Clean up comments

* Address feedback

Co-authored-by: Andrew Hershberger <andrew.hershberger@mapbox.com>
  • Loading branch information
jmkiley and macdrevx committed Nov 16, 2021
1 parent 7c2c073 commit 2ab4653
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 180 deletions.
42 changes: 38 additions & 4 deletions Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public protocol AnnotationManager: AnyObject {
}

internal protocol AnnotationManagerInternal: AnnotationManager {
var delegate: AnnotationInteractionDelegate? { get }

func destroy()

func handleQueriedFeatureIds(_ queriedFeatureIds: [String])
}

/// A delegate that is called when a tap is detected on an annotation (or on several of them).
Expand Down Expand Up @@ -59,6 +63,8 @@ public class AnnotationOrchestrator {
self.mapFeatureQueryable = mapFeatureQueryable
self.style = style
self.displayLinkCoordinator = displayLinkCoordinator

gestureRecognizer.addTarget(self, action: #selector(handleTap(_:)))
}

/// Dictionary of annotation managers keyed by their identifiers.
Expand All @@ -85,7 +91,6 @@ public class AnnotationOrchestrator {
let annotationManager = PointAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -110,7 +115,6 @@ public class AnnotationOrchestrator {
let annotationManager = PolygonAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -135,7 +139,6 @@ public class AnnotationOrchestrator {
let annotationManager = PolylineAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -160,7 +163,6 @@ public class AnnotationOrchestrator {
let annotationManager = CircleAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -186,4 +188,36 @@ public class AnnotationOrchestrator {
category: "Annotations")
}
}

@objc private func handleTap(_ tap: UITapGestureRecognizer) {
let managers = annotationManagersByIdInternal.values.filter { $0.delegate != nil }
guard !managers.isEmpty else { return }

let layerIds = managers.map { $0.layerId }
let options = RenderedQueryOptions(layerIds: layerIds, filter: nil)
mapFeatureQueryable.queryRenderedFeatures(
at: tap.location(in: tap.view),
options: options) { (result) in

switch result {

case .success(let queriedFeatures):

// Get the identifiers of all the queried features
let queriedFeatureIds: [String] = queriedFeatures.compactMap {
guard case let .string(featureId) = $0.feature.identifier else {
return nil
}
return featureId
}

for manager in managers {
manager.handleQueriedFeatureIds(queriedFeatureIds)
}
case .failure(let error):
Log.warning(forMessage: "Failed to query map for annotations due to error: \(error)",
category: "Annotations")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,20 @@ public class CircleAnnotationManager: AnnotationManagerInternal {

private weak var displayLinkCoordinator: DisplayLinkCoordinator?

private let gestureRecognizer: UIGestureRecognizer

private var isDestroyed = false

internal init(id: String,
style: Style,
gestureRecognizer: UIGestureRecognizer,
mapFeatureQueryable: MapFeatureQueryable,
layerPosition: LayerPosition?,
displayLinkCoordinator: DisplayLinkCoordinator) {
self.id = id
self.sourceId = id
self.layerId = id
self.style = style
self.gestureRecognizer = gestureRecognizer
self.mapFeatureQueryable = mapFeatureQueryable
self.displayLinkCoordinator = displayLinkCoordinator

// Add target-action for tap handling
gestureRecognizer.addTarget(self, action: #selector(handleTap(_:)))

do {
// Add the source with empty `data` property
var source = GeoJSONSource()
Expand All @@ -101,7 +94,7 @@ public class CircleAnnotationManager: AnnotationManagerInternal {
return
}
isDestroyed = true
gestureRecognizer.removeTarget(self, action: nil)

do {
try style.removeLayer(withId: layerId)
} catch {
Expand Down Expand Up @@ -218,43 +211,15 @@ public class CircleAnnotationManager: AnnotationManagerInternal {
}
}

@objc private func handleTap(_ tap: UITapGestureRecognizer) {

guard delegate != nil else { return }

let options = RenderedQueryOptions(layerIds: [layerId], filter: nil)
mapFeatureQueryable.queryRenderedFeatures(
at: tap.location(in: tap.view),
options: options) { [weak self] (result) in

guard let self = self else { return }

switch result {

case .success(let queriedFeatures):

// Get the identifiers of all the queried features
let queriedFeatureIds: [String] = queriedFeatures.compactMap {
guard case let .string(featureId) = $0.feature.identifier else {
return nil
}
return featureId
}

// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = self.annotations.filter { queriedFeatureIds.contains($0.id) }

// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
self.delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
internal func handleQueriedFeatureIds(_ queriedFeatureIds: [String]) {
// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = annotations.filter { queriedFeatureIds.contains($0.id) }

case .failure(let error):
Log.warning(forMessage: "Failed to query map for annotations due to error: \(error)",
category: "Annotations")
}
// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,20 @@ public class PointAnnotationManager: AnnotationManagerInternal {

private weak var displayLinkCoordinator: DisplayLinkCoordinator?

private let gestureRecognizer: UIGestureRecognizer

private var isDestroyed = false

internal init(id: String,
style: Style,
gestureRecognizer: UIGestureRecognizer,
mapFeatureQueryable: MapFeatureQueryable,
layerPosition: LayerPosition?,
displayLinkCoordinator: DisplayLinkCoordinator) {
self.id = id
self.sourceId = id
self.layerId = id
self.style = style
self.gestureRecognizer = gestureRecognizer
self.mapFeatureQueryable = mapFeatureQueryable
self.displayLinkCoordinator = displayLinkCoordinator

// Add target-action for tap handling
gestureRecognizer.addTarget(self, action: #selector(handleTap(_:)))

do {
// Add the source with empty `data` property
var source = GeoJSONSource()
Expand Down Expand Up @@ -107,7 +100,7 @@ public class PointAnnotationManager: AnnotationManagerInternal {
return
}
isDestroyed = true
gestureRecognizer.removeTarget(self, action: nil)

do {
try style.removeLayer(withId: layerId)
} catch {
Expand Down Expand Up @@ -476,43 +469,15 @@ public class PointAnnotationManager: AnnotationManagerInternal {
}
}

@objc private func handleTap(_ tap: UITapGestureRecognizer) {

guard delegate != nil else { return }

let options = RenderedQueryOptions(layerIds: [layerId], filter: nil)
mapFeatureQueryable.queryRenderedFeatures(
at: tap.location(in: tap.view),
options: options) { [weak self] (result) in

guard let self = self else { return }

switch result {

case .success(let queriedFeatures):

// Get the identifiers of all the queried features
let queriedFeatureIds: [String] = queriedFeatures.compactMap {
guard case let .string(featureId) = $0.feature.identifier else {
return nil
}
return featureId
}

// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = self.annotations.filter { queriedFeatureIds.contains($0.id) }

// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
self.delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
internal func handleQueriedFeatureIds(_ queriedFeatureIds: [String]) {
// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = annotations.filter { queriedFeatureIds.contains($0.id) }

case .failure(let error):
Log.warning(forMessage: "Failed to query map for annotations due to error: \(error)",
category: "Annotations")
}
// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,20 @@ public class PolygonAnnotationManager: AnnotationManagerInternal {

private weak var displayLinkCoordinator: DisplayLinkCoordinator?

private let gestureRecognizer: UIGestureRecognizer

private var isDestroyed = false

internal init(id: String,
style: Style,
gestureRecognizer: UIGestureRecognizer,
mapFeatureQueryable: MapFeatureQueryable,
layerPosition: LayerPosition?,
displayLinkCoordinator: DisplayLinkCoordinator) {
self.id = id
self.sourceId = id
self.layerId = id
self.style = style
self.gestureRecognizer = gestureRecognizer
self.mapFeatureQueryable = mapFeatureQueryable
self.displayLinkCoordinator = displayLinkCoordinator

// Add target-action for tap handling
gestureRecognizer.addTarget(self, action: #selector(handleTap(_:)))

do {
// Add the source with empty `data` property
var source = GeoJSONSource()
Expand All @@ -101,7 +94,7 @@ public class PolygonAnnotationManager: AnnotationManagerInternal {
return
}
isDestroyed = true
gestureRecognizer.removeTarget(self, action: nil)

do {
try style.removeLayer(withId: layerId)
} catch {
Expand Down Expand Up @@ -208,43 +201,15 @@ public class PolygonAnnotationManager: AnnotationManagerInternal {
}
}

@objc private func handleTap(_ tap: UITapGestureRecognizer) {

guard delegate != nil else { return }

let options = RenderedQueryOptions(layerIds: [layerId], filter: nil)
mapFeatureQueryable.queryRenderedFeatures(
at: tap.location(in: tap.view),
options: options) { [weak self] (result) in

guard let self = self else { return }

switch result {

case .success(let queriedFeatures):

// Get the identifiers of all the queried features
let queriedFeatureIds: [String] = queriedFeatures.compactMap {
guard case let .string(featureId) = $0.feature.identifier else {
return nil
}
return featureId
}

// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = self.annotations.filter { queriedFeatureIds.contains($0.id) }

// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
self.delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
internal func handleQueriedFeatureIds(_ queriedFeatureIds: [String]) {
// Find if any `queriedFeatureIds` match an annotation's `id`
let tappedAnnotations = annotations.filter { queriedFeatureIds.contains($0.id) }

case .failure(let error):
Log.warning(forMessage: "Failed to query map for annotations due to error: \(error)",
category: "Annotations")
}
// If `tappedAnnotations` is not empty, call delegate
if !tappedAnnotations.isEmpty {
delegate?.annotationManager(
self,
didDetectTappedAnnotations: tappedAnnotations)
}
}
}
Expand Down
Loading

0 comments on commit 2ab4653

Please sign in to comment.