diff --git a/Sources/CustomAnnotatedMap/Helpers/MapKit.swift b/Sources/CustomAnnotatedMap/Helpers/MapKit.swift new file mode 100644 index 0000000..f785b95 --- /dev/null +++ b/Sources/CustomAnnotatedMap/Helpers/MapKit.swift @@ -0,0 +1,36 @@ +// +// MapKit.swift +// +// +// Created by Jiří Buček on 28.07.2022. +// + +import Foundation +import MapKit + +extension CLLocationCoordinate2D { + + func equals(to other: CLLocationCoordinate2D) -> Bool { + latitude == other.latitude + && longitude == other.longitude + } + +} + +extension MKCoordinateRegion { + + func equals(to other: MKCoordinateRegion) -> Bool { + center.equals(to: other.center) + && span.equals(to: other.span) + } + +} + +extension MKCoordinateSpan { + + func equals(to other: MKCoordinateSpan) -> Bool { + latitudeDelta == other.latitudeDelta + && longitudeDelta == other.longitudeDelta + } + +} diff --git a/Sources/CustomAnnotatedMap/_CustomAnnotatedMapContent.swift b/Sources/CustomAnnotatedMap/_CustomAnnotatedMapContent.swift index e9dcd8a..f09c1fd 100644 --- a/Sources/CustomAnnotatedMap/_CustomAnnotatedMapContent.swift +++ b/Sources/CustomAnnotatedMap/_CustomAnnotatedMapContent.swift @@ -108,17 +108,8 @@ where updateAnnotationsIfNeeded(on: mapView, with: context.coordinator) - if mapView.showsUserLocation != self.showsUserLocation { - mapView.showsUserLocation = self.showsUserLocation - } - - if let userTrackingMode = MKUserTrackingMode(rawValue: self.userTrackingMode.rawValue), - mapView.userTrackingMode != userTrackingMode - { - performWithoutLocationUpdates(on: context.coordinator) { - mapView.setUserTrackingMode(userTrackingMode, animated: true) - } - } + updateUserTrackingMode(on: mapView, + with: context.coordinator) // Update the map region either using the coordinateRegion or MapRect if let coordinateRegion = self.coordinateRegion { @@ -134,6 +125,24 @@ where } } + /// Updates the user tracking mode when it is different than the currently set mode + /// - Parameters: + /// - mapView: The MKMapView associated with this UIViewRepresentable + /// - coordinator: The associated coordinator object + private func updateUserTrackingMode(on mapView: MKMapView, with coordinator: Coordinator) { + DispatchQueue.main.async { + if mapView.showsUserLocation != self.showsUserLocation { + mapView.showsUserLocation = self.showsUserLocation + } + + if let userTrackingMode = MKUserTrackingMode(rawValue: self.userTrackingMode.rawValue), + mapView.userTrackingMode != userTrackingMode + { + mapView.setUserTrackingMode(userTrackingMode, animated: true) + } + } + } + /// Checks if the current annotations reflect the annotations displayed in the map and updates accordingly /// - Parameters: @@ -160,28 +169,16 @@ where /// - coordinateRegion: The region the map will be moved to /// - mapView: The MKMapView associated with this UIViewRepresentable /// - coordinator: The associated coordinator object - private func updateRegion(_ coordinateRegion: CoordinateRegion, on mapView: UIViewType, with coordinator: Coordinator) { - // Prevents repetitive calls to MKMapView.setRegion when other parts of the view are updated that are not - // related to the map region - guard !coordinateRegion.mapRect.isSame(as: coordinator.lastMapRect) else { + private func updateRegion(_ coordinateRegion: CoordinateRegion, + on mapView: UIViewType, + with coordinator: Coordinator) { + DispatchQueue.main.async { + guard !coordinateRegion.rawValue.equals(to: mapView.region), + !coordinator.regionIsChanging + else { return } - - performWithoutLocationUpdates(on: coordinator) { - mapView.setRegion(coordinateRegion.rawValue, animated: true) - } - } - - /// Pauses the location updates on the coordinator while performing a task - /// - Parameters: - /// - coordinator: The associated Coordinator object - /// - task: The task to be performed - private func performWithoutLocationUpdates(on coordinator: _CustomAnnotatedMapCoordinator, task: () -> Void) { - defer { - coordinator.listenToLocationChanges = true + mapView.setRegion(coordinateRegion.rawValue, animated: true) } - - coordinator.listenToLocationChanges = false - task() } } diff --git a/Sources/CustomAnnotatedMap/_CustomAnnotatedMapCoordinator.swift b/Sources/CustomAnnotatedMap/_CustomAnnotatedMapCoordinator.swift index c911889..8193b66 100644 --- a/Sources/CustomAnnotatedMap/_CustomAnnotatedMapCoordinator.swift +++ b/Sources/CustomAnnotatedMap/_CustomAnnotatedMapCoordinator.swift @@ -6,11 +6,8 @@ extension _CustomAnnotatedMapContent { public class _CustomAnnotatedMapCoordinator: NSObject, MKMapViewDelegate { private var mapContent: _CustomAnnotatedMapContent - /// Determines if changes in map region are updated to the mapContent view - var listenToLocationChanges = false - - /// The latest map region that got updated to the mapContent view - var lastMapRect: MapRect? + /// Determines if the map region is currently being changed with animation (the map is sliding to a new region) + var regionIsChanging = false /// The IDs of the annotations currently displayed in the mapContent view var displayedAnnotationsIDs: Set = [] @@ -19,21 +16,21 @@ extension _CustomAnnotatedMapContent { self.mapContent = mapContent super.init() } - - public func mapView( - _ mapView: MKMapView, - regionDidChangeAnimated animated: Bool - ) { - /* - Only update the map region changes to the mapContent if allowed. - This prevents updates during the manual changes to the map region and user tracking mode - which are animated and would be interrupted. - */ - guard listenToLocationChanges else { return } - + + /// Called multiple times when the map region change animation is taking place + public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { self.mapContent.coordinateRegion = CoordinateRegion(rawValue: mapView.region) self.mapContent.mapRect = MapRect.init(rawValue: mapView.visibleMapRect) - self.lastMapRect = MapRect.init(rawValue: mapView.visibleMapRect) + } + + /// The map view is about to change the visible map region + public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) { + regionIsChanging = true + } + + /// The map view finished changing the visible map region + public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { + regionIsChanging = false } public func mapView(