Skip to content

Commit

Permalink
Merge pull request #33 from Rallista/feat/map-view-port
Browse files Browse the repository at this point in the history
MapViewPort implementation to listen to raw values of 'camera'
  • Loading branch information
ianthetechie authored Apr 16, 2024
2 parents 6e0cf32 + 42e6907 commit c7fc6a0
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 2 deletions.
4 changes: 3 additions & 1 deletion Sources/MapLibreSwiftUI/MapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct MapView: UIViewRepresentable {
var gestures = [MapGesture]()

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

public var mapViewContentInset: UIEdgeInsets = .zero

Expand Down Expand Up @@ -42,7 +43,8 @@ public struct MapView: UIViewRepresentable {
public func makeCoordinator() -> MapViewCoordinator {
MapViewCoordinator(
parent: self,
onGesture: { processGesture($0, $1) }
onGesture: { processGesture($0, $1) },
onViewPortChanged: { onViewPortChanged?($0) }
)
}

Expand Down
26 changes: 25 additions & 1 deletion Sources/MapLibreSwiftUI/MapViewCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class MapViewCoordinator: NSObject {

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

init(parent: MapView,
onGesture: @escaping (MLNMapView, UIGestureRecognizer) -> Void)
onGesture: @escaping (MLNMapView, UIGestureRecognizer) -> Void,
onViewPortChanged: @escaping (MapViewPort) -> Void)
{
self.parent = parent
self.onGesture = onGesture
self.onViewPortChanged = onViewPortChanged
}

// MARK: Core UIView Functionality
Expand Down Expand Up @@ -205,6 +208,8 @@ extension MapViewCoordinator: MLNMapViewDelegate {
onStyleLoaded?(mglStyle)
}

// MARK: MapViewCamera

@MainActor private func updateParentCamera(mapView: MLNMapView, reason: MLNCameraChangeReason) {
// If any of these are a mismatch, we know the camera is no longer following a desired method, so we should
// detach and revert to a .centered camera. If any one of these is true, the desired camera state still
Expand Down Expand Up @@ -247,6 +252,12 @@ extension MapViewCoordinator: MLNMapViewDelegate {

/// The MapView's region has changed with a specific reason.
public func mapView(_ mapView: MLNMapView, regionDidChangeWith reason: MLNCameraChangeReason, animated _: Bool) {
// FIXME: CI complains about MainActor.assumeIsolated being unavailable before iOS 17, despite building on iOS 17.2... This is an epic hack to fix it for now. I can only assume this is an issue with Xcode pre-15.3
// TODO: We could put this in regionIsChangingWith if we calculate significant change/debounce.
Task { @MainActor in
updateViewPort(mapView: mapView)
}

guard !suppressCameraUpdatePropagation else {
return
}
Expand All @@ -256,4 +267,17 @@ extension MapViewCoordinator: MLNMapViewDelegate {
updateParentCamera(mapView: mapView, reason: reason)
}
}

// MARK: MapViewPort

@MainActor private func updateViewPort(mapView: MLNMapView) {
// Calculate the Raw "ViewPort"
let calculatedViewPort = MapViewPort(
center: mapView.centerCoordinate,
zoom: mapView.zoomLevel,
direction: mapView.direction
)

onViewPortChanged(calculatedViewPort)
}
}
6 changes: 6 additions & 0 deletions Sources/MapLibreSwiftUI/MapViewModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,10 @@ public extension MapView {

return result
}

func onMapViewPortUpdate(_ onViewPortChanged: @escaping (MapViewPort) -> Void) -> Self {
var result = self
result.onViewPortChanged = onViewPortChanged
return result
}
}
41 changes: 41 additions & 0 deletions Sources/MapLibreSwiftUI/Models/MapViewPort.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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

public init(center: CLLocationCoordinate2D, zoom: Double, direction: CLLocationDirection) {
self.center = center
self.zoom = zoom
self.direction = direction
}

public static func zero(zoom: Double = 10) -> MapViewPort {
MapViewPort(center: CLLocationCoordinate2D(latitude: 0, longitude: 0),
zoom: zoom,
direction: 0)
}
}

public extension MapViewPort {
/// Generate a basic MapViewCamera that represents the MapViewPort
///
/// - Returns: The calculated MapViewCamera
func asMapViewCamera() -> MapViewCamera {
.center(center,
zoom: zoom,
direction: direction)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ final class MapViewCoordinatorCameraTests: XCTestCase {
mapView = MapView(styleURL: URL(string: "https://maplibre.org")!)
coordinator = MapView.Coordinator(parent: mapView) { _, _ in
// No action
} onViewPortChanged: { _ in
// No action
}
}

Expand Down

0 comments on commit c7fc6a0

Please sign in to comment.