From dfc7a8d845fd8cab894b20d4abc4b40af425b4eb Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Wed, 27 Apr 2022 19:07:56 +0200 Subject: [PATCH 1/3] Add DynamicRendererMapOverlay --- .../Overlays/DynamicRendererMapOverlay.swift | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Sources/Overlays/DynamicRendererMapOverlay.swift diff --git a/Sources/Overlays/DynamicRendererMapOverlay.swift b/Sources/Overlays/DynamicRendererMapOverlay.swift new file mode 100644 index 0000000..65e2793 --- /dev/null +++ b/Sources/Overlays/DynamicRendererMapOverlay.swift @@ -0,0 +1,122 @@ +// +// DynamicRendererMapOverlay.swift +// Map +// +// Created by Paul Kraft on 26.04.22. +// + +#if !os(watchOS) + +import Combine +import MapKit + +public struct DynamicRendererMapOverlay: MapOverlay { + + // MARK: Nested Types + + public struct DisplayRequest { + + // MARK: Stored Properties + + let mapRect: MKMapRect? + let zoomScale: MKZoomScale? + + // MARK: Initialization + + public init(mapRect: MKMapRect? = nil) { + self.mapRect = mapRect + self.zoomScale = nil + } + + public init(mapRect: MKMapRect, zoomScale: MKZoomScale?) { + self.mapRect = mapRect + self.zoomScale = zoomScale + } + + } + + // MARK: Stored Properties + + public let overlay: MKOverlay + public let level: MKOverlayLevel? + private let displayRequestPublisher: AnyPublisher + private let canDraw: ((MKMapRect, MKZoomScale) -> Bool)? + private let draw: (MKMapRect, MKZoomScale, CGContext) -> Void + + // MARK: Initialization + + public init( + overlay: MKOverlay, + level: MKOverlayLevel? = nil, + publisher: P, + canDraw: ((MKMapRect, MKZoomScale) -> Bool)? = nil, + draw: @escaping (MKMapRect, MKZoomScale, CGContext) -> Void + ) where P.Output == DisplayRequest, P.Failure == Never { + + self.overlay = overlay + self.level = level + self.displayRequestPublisher = publisher.eraseToAnyPublisher() + self.canDraw = canDraw + self.draw = draw + } + + + public func renderer(for mapView: MKMapView) -> MKOverlayRenderer { + DynamicMapRenderer( + overlay: overlay, + displayRequestPublisher: displayRequestPublisher, + canDraw: canDraw, + draw: draw + ) + } + +} + +private class DynamicMapRenderer: MKOverlayRenderer { + + // MARK: Stored Properties + + let _canDraw: ((MKMapRect, MKZoomScale) -> Bool)? + let _draw: (MKMapRect, MKZoomScale, CGContext) -> Void + var cancellables = Set() + + // MARK: Initialization + + init(overlay: MKOverlay, + displayRequestPublisher: AnyPublisher, + canDraw: ((MKMapRect, MKZoomScale) -> Bool)?, + draw: @escaping (MKMapRect, MKZoomScale, CGContext) -> Void) { + + self._canDraw = canDraw + self._draw = draw + super.init(overlay: overlay) + + displayRequestPublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] request in + guard let mapRect = request.mapRect else { + self.setNeedsDisplay() + return + } + guard let zoomScale = request.zoomScale else { + self.setNeedsDisplay(mapRect) + return + } + self.setNeedsDisplay(mapRect, zoomScale: zoomScale) + } + .store(in: &cancellables) + } + + // MARK: Overrides + + override func canDraw(_ mapRect: MKMapRect, zoomScale: MKZoomScale) -> Bool { + _canDraw?(mapRect, zoomScale) ?? super.canDraw(mapRect, zoomScale: zoomScale) + } + + override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { + _draw(mapRect, zoomScale, context) + } + +} + +#endif From fec9857118be74e6ad00d663361e834ba1cb5e43 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 29 Apr 2022 14:41:07 +0200 Subject: [PATCH 2/3] Update DynamicRendererMapOverlay.swift --- .../Overlays/DynamicRendererMapOverlay.swift | 99 ++++++++++++++----- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/Sources/Overlays/DynamicRendererMapOverlay.swift b/Sources/Overlays/DynamicRendererMapOverlay.swift index 65e2793..fcb3d81 100644 --- a/Sources/Overlays/DynamicRendererMapOverlay.swift +++ b/Sources/Overlays/DynamicRendererMapOverlay.swift @@ -13,6 +13,28 @@ import MapKit public struct DynamicRendererMapOverlay: MapOverlay { // MARK: Nested Types + + public struct Update { + + // MARK: Stored Properties + + let coordinate: CLLocationCoordinate2D? + let boundingMapRect: MKMapRect? + let displayRequest: DisplayRequest? + + // MARK: Initialization + + public init( + coordinate: CLLocationCoordinate2D? = nil, + boundingMapRect: MKMapRect? = nil, + displayRequest: DisplayRequest? = nil + ) { + self.coordinate = coordinate + self.boundingMapRect = boundingMapRect + self.displayRequest = displayRequest + } + + } public struct DisplayRequest { @@ -34,26 +56,43 @@ public struct DynamicRendererMapOverlay: MapOverlay { } } + + class Overlay: NSObject, MKOverlay { + + // MARK: Stored Properties + + @objc dynamic var coordinate: CLLocationCoordinate2D + @objc dynamic var boundingMapRect: MKMapRect + + // MARK: Initialization + + init(coordinate: CLLocationCoordinate2D, boundingMapRect: MKMapRect) { + self.coordinate = coordinate + self.boundingMapRect = boundingMapRect + } + + } // MARK: Stored Properties public let overlay: MKOverlay public let level: MKOverlayLevel? - private let displayRequestPublisher: AnyPublisher + private let updatePublisher: AnyPublisher private let canDraw: ((MKMapRect, MKZoomScale) -> Bool)? private let draw: (MKMapRect, MKZoomScale, CGContext) -> Void // MARK: Initialization public init( - overlay: MKOverlay, + coordinate: CLLocationCoordinate2D, + boundingMapRect: MKMapRect, level: MKOverlayLevel? = nil, - publisher: P, + updates: P, canDraw: ((MKMapRect, MKZoomScale) -> Bool)? = nil, draw: @escaping (MKMapRect, MKZoomScale, CGContext) -> Void - ) where P.Output == DisplayRequest, P.Failure == Never { + ) where P.Output == Update, P.Failure == Never { - self.overlay = overlay + self.overlay = Overlay(coordinate: coordinate, boundingMapRect: boundingMapRect) self.level = level self.displayRequestPublisher = publisher.eraseToAnyPublisher() self.canDraw = canDraw @@ -76,14 +115,14 @@ private class DynamicMapRenderer: MKOverlayRenderer { // MARK: Stored Properties - let _canDraw: ((MKMapRect, MKZoomScale) -> Bool)? - let _draw: (MKMapRect, MKZoomScale, CGContext) -> Void - var cancellables = Set() + private let _canDraw: ((MKMapRect, MKZoomScale) -> Bool)? + private let _draw: (MKMapRect, MKZoomScale, CGContext) -> Void + private var cancellables = Set() // MARK: Initialization - init(overlay: MKOverlay, - displayRequestPublisher: AnyPublisher, + init(overlay: DynamicRendererMapOverlay.Overlay, + updatePublisher: AnyPublisher, canDraw: ((MKMapRect, MKZoomScale) -> Bool)?, draw: @escaping (MKMapRect, MKZoomScale, CGContext) -> Void) { @@ -91,19 +130,9 @@ private class DynamicMapRenderer: MKOverlayRenderer { self._draw = draw super.init(overlay: overlay) - displayRequestPublisher + updatePublisher .receive(on: DispatchQueue.main) - .sink { [unowned self] request in - guard let mapRect = request.mapRect else { - self.setNeedsDisplay() - return - } - guard let zoomScale = request.zoomScale else { - self.setNeedsDisplay(mapRect) - return - } - self.setNeedsDisplay(mapRect, zoomScale: zoomScale) - } + .sink { [unowned self] in self.handle($0) } .store(in: &cancellables) } @@ -116,6 +145,32 @@ private class DynamicMapRenderer: MKOverlayRenderer { override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { _draw(mapRect, zoomScale, context) } + + // MARK: Helpers + + private func handle(_ update: DynamicRendererMapOverlay.Update) { + let typedOverlay = overlay as? DynamicRendererMapOverlay.Overlay + + if let coordinate = update.coordinate { + typedOverlay?.coordinate = coordinate + } + + if let boundingMapRect = update.boundingMapRect { + typedOverlay?.boundingMapRect = boundingMapRect + } + + if let request = update.displayRequest { + guard let mapRect = request.mapRect else { + setNeedsDisplay() + return + } + guard let zoomScale = request.zoomScale else { + setNeedsDisplay(mapRect) + return + } + setNeedsDisplay(mapRect, zoomScale: zoomScale) + } + } } From 5a9526f980fd80d4e79289817be4817e74c16d91 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:00:30 +0200 Subject: [PATCH 3/3] Fix DynamicRendererMapOverlay --- Sources/Overlays/DynamicRendererMapOverlay.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/Overlays/DynamicRendererMapOverlay.swift b/Sources/Overlays/DynamicRendererMapOverlay.swift index fcb3d81..e1edce4 100644 --- a/Sources/Overlays/DynamicRendererMapOverlay.swift +++ b/Sources/Overlays/DynamicRendererMapOverlay.swift @@ -94,16 +94,20 @@ public struct DynamicRendererMapOverlay: MapOverlay { self.overlay = Overlay(coordinate: coordinate, boundingMapRect: boundingMapRect) self.level = level - self.displayRequestPublisher = publisher.eraseToAnyPublisher() + self.updatePublisher = updates.eraseToAnyPublisher() self.canDraw = canDraw self.draw = draw } public func renderer(for mapView: MKMapView) -> MKOverlayRenderer { - DynamicMapRenderer( + guard let overlay = overlay as? Overlay else { + assertionFailure("DynamicRendererMapOverlay does not support custom MKOverlay classes.") + return MKOverlayRenderer(overlay: overlay) + } + return DynamicMapRenderer( overlay: overlay, - displayRequestPublisher: displayRequestPublisher, + updatePublisher: updatePublisher, canDraw: canDraw, draw: draw ) @@ -155,7 +159,7 @@ private class DynamicMapRenderer: MKOverlayRenderer { typedOverlay?.coordinate = coordinate } - if let boundingMapRect = update.boundingMapRect { + if let boundingMapRect = update.boundingMapRect { typedOverlay?.boundingMapRect = boundingMapRect }