Skip to content

Commit

Permalink
Generic annotations refinement (#2263)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksproger committed Aug 14, 2024
1 parent cb1ed93 commit cc7bbe9
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 188 deletions.
50 changes: 48 additions & 2 deletions Sources/MapboxMaps/Annotations/Annotation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,67 @@ public protocol Annotation {
}

protocol AnnotationInternal {
associatedtype GeometryType: GeometryConvertible
associatedtype LayerType: Layer
associatedtype GeometryType: GeometryConvertible & OffsetGeometryCalculator

var id: String { get set }
var layerProperties: [String: Any] { get }
var feature: Feature { get }
var isSelected: Bool { get set }
var isDraggable: Bool { get set }
var _geometry: GeometryType { get set }

var tapHandler: ((MapContentGestureContext) -> Bool)? { get set }
var longPressHandler: ((MapContentGestureContext) -> Bool)? { get set }

var dragBeginHandler: ((inout Self, MapContentGestureContext) -> Bool)? { get set }
var dragChangeHandler: ((inout Self, MapContentGestureContext) -> Void)? { get set }
var dragEndHandler: ((inout Self, MapContentGestureContext) -> Void)? { get set }

mutating func drag(translation: CGPoint, in map: MapboxMapProtocol)

static func makeLayer(id: String) -> LayerType
}

extension PointAnnotation {
typealias GeometryType = Point
typealias LayerType = SymbolLayer

static func makeLayer(id: String) -> SymbolLayer {
var layer = SymbolLayer(id: id, source: id)
// Show all icons and texts by default in point annotations.
layer.iconAllowOverlap = .constant(true)
layer.textAllowOverlap = .constant(true)
layer.iconIgnorePlacement = .constant(true)
layer.textIgnorePlacement = .constant(true)
return layer
}
}

extension CircleAnnotation {
typealias GeometryType = Point
typealias LayerType = CircleLayer

static func makeLayer(id: String) -> CircleLayer {
CircleLayer(id: id, source: id)
}
}

extension PolygonAnnotation {
typealias GeometryType = Polygon
typealias LayerType = FillLayer

static func makeLayer(id: String) -> FillLayer {
FillLayer(id: id, source: id)
}
}

extension PolylineAnnotation {
typealias GeometryType = LineString
typealias LayerType = LineLayer

static func makeLayer(id: String) -> LineLayer {
LineLayer(id: id, source: id)
}
}

extension Array where Element: Annotation {
Expand Down
53 changes: 25 additions & 28 deletions Sources/MapboxMaps/Annotations/AnnotationManagerImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ protocol AnnotationManagerImplProtocol {
func handleDragEnd(context: MapContentGestureContext)
}

final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationManagerImplProtocol {
typealias AnnotationType = Traits.AnnotationType

final class AnnotationManagerImpl<AnnotationType: Annotation & AnnotationInternal & Equatable>: AnnotationManagerImplProtocol {
let id: String

weak var delegate: AnnotationManagerImplDelegate?

var annotations: [Traits.AnnotationType] {
var annotations: [AnnotationType] {
get { mainAnnotations + draggedAnnotations }
set {
mainAnnotations = newValue
Expand All @@ -40,8 +38,8 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa

// Deps
private let style: StyleProtocol
private let offsetCalculator: Traits.OffsetCalculator
private let mapFeatureQueryable: MapFeatureQueryable
private let mapboxMap: MapboxMapProtocol
private let clusterOptions: ClusterOptions?
private let layerType: LayerType

Expand All @@ -54,15 +52,15 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
private weak var _delegate: AnnotationInteractionDelegate?

/// Currently displayed (synced) annotations.
private var displayedAnnotations: [Traits.AnnotationType] = []
private var displayedAnnotations: [AnnotationType] = []

/// Updated, non-moved annotations. On next display link they will be diffed with `displayedAnnotations` and updated.
private var mainAnnotations = [Traits.AnnotationType]() {
private var mainAnnotations = [AnnotationType]() {
didSet { syncSourceOnce.reset() }
}

/// When annotation is moved for the first time, it migrates to this array from mainAnnotations.
private var draggedAnnotations = [Traits.AnnotationType]() {
private var draggedAnnotations = [AnnotationType]() {
didSet {
if insertDraggedLayerAndSourceOnce.happened {
// Update dragged annotation only when the drag layer is created.
Expand Down Expand Up @@ -109,7 +107,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
init(params: AnnotationManagerParams, deps: AnnotationManagerDeps) {
self.id = params.id
self.style = deps.style
self.offsetCalculator = deps.makeOffsetCalculator()
self.mapboxMap = deps.map
self.layerPosition = params.layerPosition

self.clusterOptions = params.clusterOptions
Expand All @@ -127,7 +125,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
source.clusterMinPoints = clusterOptions.clusterMinPoints
}

let layer = Traits.makeLayer(id: id)
let layer = AnnotationType.makeLayer(id: id)
self.layerType = layer.type

if let clusterOptions {
Expand All @@ -140,7 +138,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
try style.addSource(source)
} catch {
Log.error(
forMessage: "Failed to create source / layer in \(Traits.tag). Error: \(error)",
forMessage: "Failed to create source / layer in \(implementationName). Error: \(error)",
category: "Annotations")
}

Expand All @@ -157,7 +155,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
try addClusterLayer(clusterLayer: clusterTextLayer)
} catch {
Log.error(
forMessage: "Failed to add cluster layer in \(Traits.tag). Error: \(error)",
forMessage: "Failed to add cluster layer in \(implementationName). Error: \(error)",
category: "Annotations")
}
}
Expand Down Expand Up @@ -193,14 +191,14 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
try style.removeLayer(withId: "mapbox-iOS-cluster-text-layer-manager-" + id)
} catch {
Log.error(
forMessage: "Failed to remove cluster layer in \(Traits.tag). Error: \(error)",
forMessage: "Failed to remove cluster layer in \(implementationName). Error: \(error)",
category: "Annotations")
}
}

// For SwiftUI
func set(newAnnotations: [(AnyHashable, Traits.AnnotationType)]) {
var resolvedAnnotations = [Traits.AnnotationType]()
func set(newAnnotations: [(AnyHashable, AnnotationType)]) {
var resolvedAnnotations = [AnnotationType]()
newAnnotations.forEach { elementId, annotation in
var annotation = annotation
let stringId = idsMap[elementId] ?? annotation.id
Expand Down Expand Up @@ -228,7 +226,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
try body()
} catch {
Log.warning(
forMessage: "Failed to remove \(what) for PointAnnotationManager with id \(id) due to error: \(error)",
forMessage: "Failed to remove \(what) for \(implementationName) with id \(id) due to error: \(error)",
category: "Annotations")
}
}
Expand Down Expand Up @@ -257,7 +255,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
func syncSourceAndLayerIfNeeded() {
guard !destroyOnce.happened else { return }

OSLog.platform.withIntervalSignpost(SignpostName.mapViewDisplayLink, "Participant: \(Traits.tag)") {
OSLog.platform.withIntervalSignpost(SignpostName.mapViewDisplayLink, "Participant: \(implementationName)") {
syncSource()
syncDragSource()
syncLayer()
Expand Down Expand Up @@ -380,11 +378,11 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
func handleDragBegin(with featureId: String, context: MapContentGestureContext) -> Bool {
guard !isSwiftUI else { return false }

func predicate(annotation: Traits.AnnotationType) -> Bool {
func predicate(annotation: AnnotationType) -> Bool {
annotation.id == featureId && annotation.isDraggable
}

func tryBeginDragging(_ annotations: inout [Traits.AnnotationType], idx: Int) -> Bool {
func tryBeginDragging(_ annotations: inout [AnnotationType], idx: Int) -> Bool {
var annotation = annotations[idx]
// If no drag handler set, the dragging is allowed
let dragAllowed = annotation.dragBeginHandler?(&annotation, context) ?? true
Expand Down Expand Up @@ -424,11 +422,9 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa

private func insertDraggedLayerAndSource() {
insertDraggedLayerAndSourceOnce {
let source = GeoJSONSource(id: dragId)
let layer = Traits.makeLayer(id: dragId)
do {
try style.addSource(source)
try style.addPersistentLayer(layer, layerPosition: .above(id))
try style.addSource(GeoJSONSource(id: dragId))
try style.addPersistentLayer(AnnotationType.makeLayer(id: dragId), layerPosition: .above(id))
} catch {
Log.error(forMessage: "Add drag source/layer \(error)", category: "Annotations")
}
Expand All @@ -443,10 +439,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
return
}

let currentGeometry = draggedAnnotations[draggedAnnotationIndex]._geometry
guard let geometry = offsetCalculator.geometry(for: translation, from: currentGeometry) else { return }
draggedAnnotations[draggedAnnotationIndex]._geometry = geometry

draggedAnnotations[draggedAnnotationIndex].drag(translation: translation, in: mapboxMap)
callDragHandler(\.dragChangeHandler, context: context)
}

Expand All @@ -457,7 +450,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
}

private func callDragHandler(
_ keyPath: KeyPath<Traits.AnnotationType, ((inout Traits.AnnotationType, MapContentGestureContext) -> Void)?>,
_ keyPath: KeyPath<AnnotationType, ((inout AnnotationType, MapContentGestureContext) -> Void)?>,
context: MapContentGestureContext
) {
guard let draggedAnnotationIndex, draggedAnnotationIndex < draggedAnnotations.endIndex else {
Expand All @@ -471,3 +464,7 @@ final class AnnotationManagerImpl<Traits: AnnotationManagerTraits>: AnnotationMa
}
}
}

private extension AnnotationManagerImpl {
var implementationName: String { "\(String(describing: AnnotationType.self))Manager" }
}
64 changes: 0 additions & 64 deletions Sources/MapboxMaps/Annotations/AnnotationManagerTraits.swift

This file was deleted.

8 changes: 2 additions & 6 deletions Sources/MapboxMaps/Annotations/AnnotationOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ struct AnnotationManagerDeps {
let imagesManager: AnnotationImagesManagerProtocol
let displayLink: Signal<Void>

func makeOffsetCalculator<T: OffsetGeometryCalculator>() -> T {
return T.init(mapboxMap: map)
}

static func from(mapboxMap map: MapboxMap, displayLink: Signal<Void>) -> AnnotationManagerDeps {
AnnotationManagerDeps(
map: map,
Expand All @@ -46,8 +42,8 @@ struct AnnotationManagerDeps {
}

protocol AnnotationManagerInternal: AnnotationManager {
associatedtype Traits: AnnotationManagerTraits
var impl: AnnotationManagerImpl<Traits> { get }
associatedtype AnnotationType: Annotation & AnnotationInternal & Equatable
var impl: AnnotationManagerImpl<AnnotationType> { get }
init(params: AnnotationManagerParams, deps: AnnotationManagerDeps)
}

Expand Down
15 changes: 5 additions & 10 deletions Sources/MapboxMaps/Annotations/Generated/CircleAnnotation.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit cc7bbe9

Please sign in to comment.