From ba658275606d05a818b90c99d96cdcf5e79fbc83 Mon Sep 17 00:00:00 2001 From: PW Date: Wed, 11 Dec 2024 12:18:05 +0100 Subject: [PATCH 1/3] changing MapViewPort to MapViewProxy --- Sources/MapLibreSwiftUI/MapView.swift | 4 +- .../MapLibreSwiftUI/MapViewCoordinator.swift | 23 +++--- .../MapLibreSwiftUI/MapViewModifiers.swift | 4 +- .../Models/MapCamera/CameraChangeReason.swift | 1 + .../MapLibreSwiftUI/Models/MapViewPort.swift | 50 ------------- .../MapLibreSwiftUI/Models/MapViewProxy.swift | 74 +++++++++++++++++++ .../MapViewCoordinatorCameraTests.swift | 2 +- 7 files changed, 91 insertions(+), 67 deletions(-) delete mode 100644 Sources/MapLibreSwiftUI/Models/MapViewPort.swift create mode 100644 Sources/MapLibreSwiftUI/Models/MapViewProxy.swift diff --git a/Sources/MapLibreSwiftUI/MapView.swift b/Sources/MapLibreSwiftUI/MapView.swift index 8a24ce6..acb8fd4 100644 --- a/Sources/MapLibreSwiftUI/MapView.swift +++ b/Sources/MapLibreSwiftUI/MapView.swift @@ -16,7 +16,7 @@ public struct MapView: UIViewControllerRepresentab var gestures = [MapGesture]() var onStyleLoaded: ((MLNStyle) -> Void)? - var onViewPortChanged: ((MapViewPort) -> Void)? + var onViewProxyChanged: ((MapViewProxy) -> Void)? var mapViewContentInset: UIEdgeInsets? @@ -50,7 +50,7 @@ public struct MapView: UIViewControllerRepresentab MapViewCoordinator( parent: self, onGesture: { processGesture($0, $1) }, - onViewPortChanged: { onViewPortChanged?($0) } + onViewProxyChanged: { onViewProxyChanged?($0) } ) } diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index 304f985..423e8f9 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -20,15 +20,15 @@ public class MapViewCoordinator: NSObject, MLNMapV var onStyleLoaded: ((MLNStyle) -> Void)? var onGesture: (MLNMapView, UIGestureRecognizer) -> Void - var onViewPortChanged: (MapViewPort) -> Void + var onViewProxyChanged: (MapViewProxy) -> Void init(parent: MapView, onGesture: @escaping (MLNMapView, UIGestureRecognizer) -> Void, - onViewPortChanged: @escaping (MapViewPort) -> Void) + onViewProxyChanged: @escaping (MapViewProxy) -> Void) { self.parent = parent self.onGesture = onGesture - self.onViewPortChanged = onViewPortChanged + self.onViewProxyChanged = onViewProxyChanged } // MARK: Core UIView Functionality @@ -366,7 +366,7 @@ public class MapViewCoordinator: NSObject, MLNMapV public func mapView(_ mapView: MLNMapView, regionDidChangeWith reason: MLNCameraChangeReason, animated _: Bool) { // TODO: We could put this in regionIsChangingWith if we calculate significant change/debounce. MainActor.assumeIsolated { - updateViewPort(mapView: mapView, reason: reason) + updateViewProxy(mapView: mapView, reason: reason) guard !suppressCameraUpdatePropagation else { return @@ -376,17 +376,16 @@ public class MapViewCoordinator: NSObject, MLNMapV } } - // MARK: MapViewPort + // MARK: MapViewProxy - @MainActor private func updateViewPort(mapView: MLNMapView, reason: MLNCameraChangeReason) { - // Calculate the Raw "ViewPort" - let calculatedViewPort = MapViewPort( - center: mapView.centerCoordinate, - zoom: mapView.zoomLevel, - direction: mapView.direction, + + @MainActor private func updateViewProxy(mapView: MLNMapView, reason: MLNCameraChangeReason) { + // Calculate the Raw "ViewProxy" + let calculatedViewProxy = MapViewProxy(mapView: mapView, lastReasonForChange: CameraChangeReason(reason) ) - onViewPortChanged(calculatedViewPort) + onViewProxyChanged(calculatedViewProxy) } + } diff --git a/Sources/MapLibreSwiftUI/MapViewModifiers.swift b/Sources/MapLibreSwiftUI/MapViewModifiers.swift index 29a309b..fa5f912 100644 --- a/Sources/MapLibreSwiftUI/MapViewModifiers.swift +++ b/Sources/MapLibreSwiftUI/MapViewModifiers.swift @@ -130,9 +130,9 @@ public extension MapView { return result } - func onMapViewPortUpdate(_ onViewPortChanged: @escaping (MapViewPort) -> Void) -> Self { + func onMapViewProxyUpdate(_ onViewProxyChanged: @escaping (MapViewProxy) -> Void) -> Self { var result = self - result.onViewPortChanged = onViewPortChanged + result.onViewProxyChanged = onViewProxyChanged return result } diff --git a/Sources/MapLibreSwiftUI/Models/MapCamera/CameraChangeReason.swift b/Sources/MapLibreSwiftUI/Models/MapCamera/CameraChangeReason.swift index 4212e0b..3118931 100644 --- a/Sources/MapLibreSwiftUI/Models/MapCamera/CameraChangeReason.swift +++ b/Sources/MapLibreSwiftUI/Models/MapCamera/CameraChangeReason.swift @@ -1,6 +1,7 @@ import Foundation import MapLibre +@MainActor public enum CameraChangeReason: Hashable { case programmatic case resetNorth diff --git a/Sources/MapLibreSwiftUI/Models/MapViewPort.swift b/Sources/MapLibreSwiftUI/Models/MapViewPort.swift deleted file mode 100644 index 30424df..0000000 --- a/Sources/MapLibreSwiftUI/Models/MapViewPort.swift +++ /dev/null @@ -1,50 +0,0 @@ -import CoreLocation -import Foundation - -/// A representation of the MapView's current ViewPort. -/// -/// This includes similar data to the MapViewCamera, but represents the raw -/// values associated with the MapView. This information could used to prepare -/// a new MapViewCamera on a scene, to cache the camera state, etc. -public struct MapViewPort: Hashable, Equatable { - /// The current center coordinate of the MapView - public let center: CLLocationCoordinate2D - - /// The current zoom value of the MapView - public let zoom: Double - - /// The current compass direction of the MapView - public let direction: CLLocationDirection - - /// The reason the view port was changed. - public let lastReasonForChange: CameraChangeReason? - - public init(center: CLLocationCoordinate2D, - zoom: Double, - direction: CLLocationDirection, - lastReasonForChange: CameraChangeReason?) - { - self.center = center - self.zoom = zoom - self.direction = direction - self.lastReasonForChange = lastReasonForChange - } - - public static func zero(zoom: Double = 10) -> MapViewPort { - MapViewPort(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), - zoom: zoom, - direction: 0, - lastReasonForChange: nil) - } -} - -public extension MapViewPort { - /// Generate a basic MapViewCamera that represents the MapViewPort - /// - /// - Returns: The calculated MapViewCamera - func asMapViewCamera() -> MapViewCamera { - .center(center, - zoom: zoom, - direction: direction) - } -} diff --git a/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift b/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift new file mode 100644 index 0000000..ae29928 --- /dev/null +++ b/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift @@ -0,0 +1,74 @@ +import CoreLocation +import Foundation +import MapLibre + +/// A read only representation of the MapView's current View. +/// +/// Provides access to properties and functions of the underlying MLNMapView, +/// but properties only expose their getter, and functions are only available if they +/// do no change the state of the MLNMapView. Writing directly to properties of MLNMapView +/// could clash with swiftui-dsl's state management, which is why modifiying functions +/// and properties are not exposed. +/// +/// You can use `MapView.onMapViewProxyUpdate(_ onViewProxyChanged: @escaping (MapViewProxy) -> Void)` to +/// recieve access to the MapViewProxy. +/// +/// For more information about the properties and functions, see https://maplibre.org/maplibre-native/ios/latest/documentation/maplibre/mlnmapview +@MainActor +public struct MapViewProxy: Hashable, Equatable { + /// The current center coordinate of the MapView + public var centerCoordinate: CLLocationCoordinate2D { + return mapView.centerCoordinate + } + + /// The current zoom value of the MapView + public var zoomLevel: Double { + return mapView.zoomLevel + } + + /// The current compass direction of the MapView + public var direction: CLLocationDirection { + return mapView.direction + } + + public var visibleCoordinateBounds: MLNCoordinateBounds { + return mapView.visibleCoordinateBounds + } + + public var mapViewSize: CGSize { + return mapView.frame.size + } + + public var contentInset: UIEdgeInsets { + return mapView.contentInset + } + + /// The reason the view port was changed. + public let lastReasonForChange: CameraChangeReason? + + private let mapView: MLNMapView + + public func convert(_ coordinate: CLLocationCoordinate2D, toPointTo: UIView?) -> CGPoint { + return mapView.convert(coordinate, toPointTo: toPointTo) + + } + public init(mapView: MLNMapView, + lastReasonForChange: CameraChangeReason?) + { + self.mapView = mapView + self.lastReasonForChange = lastReasonForChange + + } + +} + +public extension MapViewProxy { + /// Generate a basic MapViewCamera that represents the MapView + /// + /// - Returns: The calculated MapViewCamera + func asMapViewCamera() -> MapViewCamera { + .center(centerCoordinate, + zoom: zoomLevel, + direction: direction) + } +} diff --git a/Tests/MapLibreSwiftUITests/MapViewCoordinator/MapViewCoordinatorCameraTests.swift b/Tests/MapLibreSwiftUITests/MapViewCoordinator/MapViewCoordinatorCameraTests.swift index 59fb355..d0d68ee 100644 --- a/Tests/MapLibreSwiftUITests/MapViewCoordinator/MapViewCoordinatorCameraTests.swift +++ b/Tests/MapLibreSwiftUITests/MapViewCoordinator/MapViewCoordinatorCameraTests.swift @@ -15,7 +15,7 @@ final class MapViewCoordinatorCameraTests: XCTestCase { mapView = MapView(styleURL: URL(string: "https://maplibre.org")!) coordinator = MapView.Coordinator(parent: mapView) { _, _ in // No action - } onViewPortChanged: { _ in + } onViewProxyChanged: { _ in // No action } } From 82c485d4a0092f5ad8555d2739d3596ab7442a8b Mon Sep 17 00:00:00 2001 From: PW Date: Wed, 11 Dec 2024 12:37:03 +0100 Subject: [PATCH 2/3] ran swiftformat --- .../MapLibreSwiftUI/MapViewCoordinator.swift | 5 +--- .../MapLibreSwiftUI/Models/MapViewProxy.swift | 29 +++++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift index 423e8f9..e629871 100644 --- a/Sources/MapLibreSwiftUI/MapViewCoordinator.swift +++ b/Sources/MapLibreSwiftUI/MapViewCoordinator.swift @@ -378,14 +378,11 @@ public class MapViewCoordinator: NSObject, MLNMapV // MARK: MapViewProxy - @MainActor private func updateViewProxy(mapView: MLNMapView, reason: MLNCameraChangeReason) { // Calculate the Raw "ViewProxy" let calculatedViewProxy = MapViewProxy(mapView: mapView, - lastReasonForChange: CameraChangeReason(reason) - ) + lastReasonForChange: CameraChangeReason(reason)) onViewProxyChanged(calculatedViewProxy) } - } diff --git a/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift b/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift index ae29928..3dcd5d0 100644 --- a/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift +++ b/Sources/MapLibreSwiftUI/Models/MapViewProxy.swift @@ -13,53 +13,52 @@ import MapLibre /// You can use `MapView.onMapViewProxyUpdate(_ onViewProxyChanged: @escaping (MapViewProxy) -> Void)` to /// recieve access to the MapViewProxy. /// -/// For more information about the properties and functions, see https://maplibre.org/maplibre-native/ios/latest/documentation/maplibre/mlnmapview +/// For more information about the properties and functions, see +/// https://maplibre.org/maplibre-native/ios/latest/documentation/maplibre/mlnmapview @MainActor public struct MapViewProxy: Hashable, Equatable { /// The current center coordinate of the MapView public var centerCoordinate: CLLocationCoordinate2D { - return mapView.centerCoordinate + mapView.centerCoordinate } /// The current zoom value of the MapView public var zoomLevel: Double { - return mapView.zoomLevel + mapView.zoomLevel } /// The current compass direction of the MapView public var direction: CLLocationDirection { - return mapView.direction + mapView.direction } - + public var visibleCoordinateBounds: MLNCoordinateBounds { - return mapView.visibleCoordinateBounds + mapView.visibleCoordinateBounds } - + public var mapViewSize: CGSize { - return mapView.frame.size + mapView.frame.size } - + public var contentInset: UIEdgeInsets { - return mapView.contentInset + mapView.contentInset } /// The reason the view port was changed. public let lastReasonForChange: CameraChangeReason? - + private let mapView: MLNMapView public func convert(_ coordinate: CLLocationCoordinate2D, toPointTo: UIView?) -> CGPoint { - return mapView.convert(coordinate, toPointTo: toPointTo) - + mapView.convert(coordinate, toPointTo: toPointTo) } + public init(mapView: MLNMapView, lastReasonForChange: CameraChangeReason?) { self.mapView = mapView self.lastReasonForChange = lastReasonForChange - } - } public extension MapViewProxy { From a99371bb168f6e4e884b2bc08b1b8e390eaa2143 Mon Sep 17 00:00:00 2001 From: PW Date: Wed, 11 Dec 2024 12:56:03 +0100 Subject: [PATCH 3/3] annotation test as mainactor --- .../Models/MapCamera/CameraChangeReasonTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/MapLibreSwiftUITests/Models/MapCamera/CameraChangeReasonTests.swift b/Tests/MapLibreSwiftUITests/Models/MapCamera/CameraChangeReasonTests.swift index 52eb1f1..ee5b742 100644 --- a/Tests/MapLibreSwiftUITests/Models/MapCamera/CameraChangeReasonTests.swift +++ b/Tests/MapLibreSwiftUITests/Models/MapCamera/CameraChangeReasonTests.swift @@ -2,6 +2,7 @@ import MapLibre import XCTest @testable import MapLibreSwiftUI +@MainActor final class CameraChangeReasonTests: XCTestCase { func testProgrammatic() { let mlnReason: MLNCameraChangeReason = [.programmatic]