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

Changing MapViewPort to MapViewProxy #65

Merged
merged 3 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Sources/MapLibreSwiftUI/MapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct MapView<T: MapViewHostViewController>: UIViewControllerRepresentab
var gestures = [MapGesture]()

var onStyleLoaded: ((MLNStyle) -> Void)?
var onViewPortChanged: ((MapViewPort) -> Void)?
var onViewProxyChanged: ((MapViewProxy) -> Void)?

var mapViewContentInset: UIEdgeInsets?

Expand Down Expand Up @@ -50,7 +50,7 @@ public struct MapView<T: MapViewHostViewController>: UIViewControllerRepresentab
MapViewCoordinator<T>(
parent: self,
onGesture: { processGesture($0, $1) },
onViewPortChanged: { onViewPortChanged?($0) }
onViewProxyChanged: { onViewProxyChanged?($0) }
)
}

Expand Down
24 changes: 10 additions & 14 deletions Sources/MapLibreSwiftUI/MapViewCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@

var onStyleLoaded: ((MLNStyle) -> Void)?
var onGesture: (MLNMapView, UIGestureRecognizer) -> Void
var onViewPortChanged: (MapViewPort) -> Void
var onViewProxyChanged: (MapViewProxy) -> Void

init(parent: MapView<T>,
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
Expand Down Expand Up @@ -268,7 +268,7 @@
}

func addLayers(to mglStyle: MLNStyle) {
for layerSpec in parent.userLayers {

Check warning on line 271 in Sources/MapLibreSwiftUI/MapViewCoordinator.swift

View workflow job for this annotation

GitHub Actions / platform=iOS Simulator,name=iPhone 16,OS=18.1

main actor-isolated property 'userLayers' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode
// DISCUSS: What preventions should we try to put in place against the user accidentally adding the same layer twice?
let newLayer = layerSpec.makeStyleLayer(style: mglStyle).makeMLNStyleLayer()

Expand Down Expand Up @@ -366,7 +366,7 @@
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)

Check warning on line 369 in Sources/MapLibreSwiftUI/MapViewCoordinator.swift

View workflow job for this annotation

GitHub Actions / platform=iOS Simulator,name=iPhone 16,OS=18.1

sending 'self' risks causing data races; this is an error in the Swift 6 language mode

guard !suppressCameraUpdatePropagation else {
return
Expand All @@ -376,17 +376,13 @@
}
}

// 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,
lastReasonForChange: CameraChangeReason(reason)
)
@MainActor private func updateViewProxy(mapView: MLNMapView, reason: MLNCameraChangeReason) {
// Calculate the Raw "ViewProxy"
let calculatedViewProxy = MapViewProxy(mapView: mapView,
lastReasonForChange: CameraChangeReason(reason))

onViewPortChanged(calculatedViewPort)
onViewProxyChanged(calculatedViewProxy)
}
}
4 changes: 2 additions & 2 deletions Sources/MapLibreSwiftUI/MapViewModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import MapLibre

@MainActor
public enum CameraChangeReason: Hashable {
case programmatic
case resetNorth
Expand Down
50 changes: 0 additions & 50 deletions Sources/MapLibreSwiftUI/Models/MapViewPort.swift

This file was deleted.

73 changes: 73 additions & 0 deletions Sources/MapLibreSwiftUI/Models/MapViewProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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 {
mapView.centerCoordinate
}

/// The current zoom value of the MapView
public var zoomLevel: Double {
mapView.zoomLevel
}

/// The current compass direction of the MapView
public var direction: CLLocationDirection {
mapView.direction
}

public var visibleCoordinateBounds: MLNCoordinateBounds {
mapView.visibleCoordinateBounds
}

public var mapViewSize: CGSize {
mapView.frame.size
}

public var contentInset: UIEdgeInsets {
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 {
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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import MapLibre
import XCTest
@testable import MapLibreSwiftUI

@MainActor
final class CameraChangeReasonTests: XCTestCase {
func testProgrammatic() {
let mlnReason: MLNCameraChangeReason = [.programmatic]
Expand Down