Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Annotations so that the gesture recognizer is managed by the AnnotationOrchestrator #840

Merged
merged 8 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ public protocol AnnotationManager: AnyObject {
}

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

func destroy()

/// Passes an array of feature IDs to the `AnnotationInteractionDelegate.annotationManager(_:didDetectTappedAnnotations:)` delegate method.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd either remove this line since it's for an internal method or say something like "allows distributing results of query rendered features to annotation managers"

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 +64,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 +92,6 @@ public class AnnotationOrchestrator {
let annotationManager = PointAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -110,7 +116,6 @@ public class AnnotationOrchestrator {
let annotationManager = PolygonAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -135,7 +140,6 @@ public class AnnotationOrchestrator {
let annotationManager = PolylineAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -160,7 +164,6 @@ public class AnnotationOrchestrator {
let annotationManager = CircleAnnotationManager(
id: id,
style: style,
gestureRecognizer: gestureRecognizer,
mapFeatureQueryable: mapFeatureQueryable,
layerPosition: layerPosition,
displayLinkCoordinator: displayLinkCoordinator)
Expand All @@ -186,4 +189,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