From 5c2e0556e72705cfc82b8f959c64a5d3f18824a6 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 00:10:24 +0200 Subject: [PATCH 01/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d47a91..2d30b28 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Map +![Map](https://user-images.githubusercontent.com/15239005/165400895-182eb850-f05a-4aa5-b525-866efd5628c5.png) MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentation/mapkit/map) (UIKit: [MKMapView](https://developer.apple.com/documentation/mapkit/mkmapview)) is very limited. This library can be used as a drop-in solution (i.e. it features a very similar, but more powerful and customizable interface) to the existing [Map](https://developer.apple.com/documentation/mapkit/map) and gives you so much more features and control: From 004f30f9727e44ebb82bdc34f2269e9729276d48 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 00:28:50 +0200 Subject: [PATCH 02/24] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d30b28..9e220c2 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,8 @@ struct MyMapView: View { .mapKey(MyMapKey()) .overlay(alignment: .topLeading) { MapScale(key: MyMapKey(), alignment: .leading, visibility: .visible) - .padding(8) + .fixedSize() + .padding(12) } } } From c44f8c6e16ba42f7c33fa6c1e4580680d637b45a Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 00:44:34 +0200 Subject: [PATCH 03/24] Update README.md --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 9e220c2..23ff40c 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,20 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentation/mapkit/map) (UIKit: [MKMapView](https://developer.apple.com/documentation/mapkit/mkmapview)) is very limited. This library can be used as a drop-in solution (i.e. it features a very similar, but more powerful and customizable interface) to the existing [Map](https://developer.apple.com/documentation/mapkit/map) and gives you so much more features and control: -## Features +## 🚀 Features -- Annotations +📍 Annotations - Create annotations from annotationItems as in the default MapKit SwiftUI implementation. - Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! -- Overlays +🖼 Overlays - Option 1: Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items - Option 2: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects -- Appearance / Behavior Customization +🛠 Appearance / Behavior Customization - Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)) - User tracking mode ([MKUserTrackingMode](https://developer.apple.com/documentation/mapkit/mkusertrackingmode)) - Interaction modes (rotation, pitch, zoom and pan) - Point of interest filter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)). -- Adapt visibility of: +👀 Adapt visibility of: - Buildings - Compass - Pitch control @@ -24,13 +24,13 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati - User heading - User location - Zoom controls -- Custom controls +🪄 Custom controls - `MapCompass` for [MKCompassButton](https://developer.apple.com/documentation/mapkit/mkcompass) - `MapPitchControl` for [MKPitchControl](https://developer.apple.com/documentation/mapkit/mkpitchcontrol) - `MapScale` for [MKScaleView](https://developer.apple.com/documentation/mapkit/mkscaleview) - `MapZoomControl` for [MKZoomControl](https://developer.apple.com/documentation/mapkit/mkzoomcontrol) -## Supported Platforms +## 💻 Supported Platforms - iOS 13+ - macOS 10.15+ @@ -39,7 +39,7 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati Keep in mind that not all features are equally available on all platforms (based on what MapKit provides) and therefore might not be available here either. However, if you can use them using UIKit, there is a very high change that it is available here as well - if not: Let me/us know by creating an issue! -## Usage on iOS, macOS and tvOS +## 🧑🏽‍💻 Usage on iOS, macOS and tvOS Very similar to MapKit's SwiftUI wrapper, you simply create a `Map` view inside the body of your view. You can define a region or mapRect, the map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)), a pointOfInterestFilter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)), interactions Modes (with values: .none, .pitch, .pan, .zoon, .rotate and .all - which can be combined as you wish) and showsUserLocation. @@ -95,7 +95,7 @@ struct MyMapView: View { } ``` -### Annotations: The modern approach +### 📍 Annotations: The modern approach You can use a collection of items conforming to `Identifiable` and a closure that maps an item to its visual representation (available types: `MapPin`, `MapMarker` and `ViewMapAnnotation` for custom annotations from any SwiftUI `View`). @@ -119,7 +119,7 @@ Map( ) ``` -### Annotations: The old-fashioned approach +### 📌 Annotations: The old-fashioned approach Moving an existing code base over to SwiftUI is hard, especially when you want to keep methods, types and properties that you have previously built. This library, therefore, allows the use of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) instead of being forced to the new `Identifiable` style. In the additional closure, you can use one of the options mentioned in the modern-approach. Alternatively, we also have an option to use your own [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations. Simply create a struct conforming to the following protocol and you are good to go. @@ -139,7 +139,7 @@ In `registerView(on:)`, your custom annotation implementation can register a cel Note: Please make sure not to create the value of the property `annotation` dynamically. You can either use an existing object or create the object in your type's initializer. Simply put: Do not make `annotation` a computed property! -### Overlays: The modern approach +### 🌃 Overlays: The modern approach Similarly to how annotations are handled, you can also use a collection of `Identifiable` and a closure mapping it to specific overlay types. These overlay types currently contain `MapCircle`, `MapMultiPolygon`, `MapMultiPolyline`, `MapPolygon` and `MapPolyline` and this list can easily be extended by creating a type conforming to the following protocol: @@ -157,7 +157,7 @@ In your implementation, the `renderer(for:)` method creates a renderer for the o Note: Please make sure not to create the value of the property `overlay` dynamically. You can either use an existing object or create the object in your type's initializer. Simply put: Do not make `overlay` a computed property! -### Overlays: The old-fashioned approach +### 🖼 Overlays: The old-fashioned approach Especially when working with [MKDirections](https://developer.apple.com/documentation/mapkit/mkdirections) or when more customization to the [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) is necessary, you can also provide an array of [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) objects and use your own [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer). @@ -182,7 +182,7 @@ Map( ) ``` -### Custom Map Controls +### 🪄 Custom Map Controls For the use of `MapCompass`, `MapPitchControl`, `MapScale` and `MapZoomControl` you will need to associate both the `Map` and the control with some form of a shared key. This key needs to conform to the `Hashable` protocol. For each key, there must only be one `Map` (or `MKMapView` respectively) in the view hierarchy at once. @@ -207,7 +207,7 @@ struct MyMapView: View { } ``` -## Usage on watchOS +## ⌚️ Usage on watchOS Since MapKit is very limited on watchOS, there is a separate (also similary limited) wrapper in this library. If you are only targeting watchOS, it might not make sense to use this library as the underlying feature set is already very limited (e.g. no overlay support, only a few kinds of possible annotations, etc). @@ -229,10 +229,10 @@ Map( ) ``` -## Author +## ✍️ Author Paul Kraft -## License +## 📄 License Map is available under the MIT license. See the LICENSE file for more info. From 8ec465ed1fc66a235051c3039a52d72799b52ef9 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 00:47:23 +0200 Subject: [PATCH 04/24] Update README.md --- README.md | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 23ff40c..b8e00f1 100644 --- a/README.md +++ b/README.md @@ -5,30 +5,34 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati ## 🚀 Features 📍 Annotations - - Create annotations from annotationItems as in the default MapKit SwiftUI implementation. - - Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! +- Create annotations from annotationItems as in the default MapKit SwiftUI implementation. +- Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! + 🖼 Overlays - - Option 1: Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items - - Option 2: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects +- Option 1: Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items +- Option 2: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects + 🛠 Appearance / Behavior Customization - - Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)) - - User tracking mode ([MKUserTrackingMode](https://developer.apple.com/documentation/mapkit/mkusertrackingmode)) - - Interaction modes (rotation, pitch, zoom and pan) - - Point of interest filter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)). +- Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)) +- User tracking mode ([MKUserTrackingMode](https://developer.apple.com/documentation/mapkit/mkusertrackingmode)) +- Interaction modes (rotation, pitch, zoom and pan) +- Point of interest filter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)). + 👀 Adapt visibility of: - - Buildings - - Compass - - Pitch control - - Scale - - Traffic - - User heading - - User location - - Zoom controls +- Buildings +- Compass +- Pitch control +- Scale +- Traffic +- User heading +- User location +- Zoom controls + 🪄 Custom controls - - `MapCompass` for [MKCompassButton](https://developer.apple.com/documentation/mapkit/mkcompass) - - `MapPitchControl` for [MKPitchControl](https://developer.apple.com/documentation/mapkit/mkpitchcontrol) - - `MapScale` for [MKScaleView](https://developer.apple.com/documentation/mapkit/mkscaleview) - - `MapZoomControl` for [MKZoomControl](https://developer.apple.com/documentation/mapkit/mkzoomcontrol) +- `MapCompass` for [MKCompassButton](https://developer.apple.com/documentation/mapkit/mkcompass) +- `MapPitchControl` for [MKPitchControl](https://developer.apple.com/documentation/mapkit/mkpitchcontrol) +- `MapScale` for [MKScaleView](https://developer.apple.com/documentation/mapkit/mkscaleview) +- `MapZoomControl` for [MKZoomControl](https://developer.apple.com/documentation/mapkit/mkzoomcontrol) ## 💻 Supported Platforms From ee731f77c4f4551952d63840d3e7130c39cac5ae Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 00:47:59 +0200 Subject: [PATCH 05/24] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b8e00f1..2e8829f 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati 📍 Annotations - Create annotations from annotationItems as in the default MapKit SwiftUI implementation. -- Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! +- Or: Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! 🖼 Overlays -- Option 1: Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items -- Option 2: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects +- Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items +- Or: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects 🛠 Appearance / Behavior Customization - Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)) From 3d3076872d47716f18b2737c49fc8bf04387dd85 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 10:41:29 +0200 Subject: [PATCH 06/24] Update README.md --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2e8829f..5715a1b 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,21 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati ## 🚀 Features -📍 Annotations +### 📍 Annotations - Create annotations from annotationItems as in the default MapKit SwiftUI implementation. - Or: Create annotations from a list of [MKAnnotation](https://developer.apple.com/documentation/mapkit/mkannotation) objects - you can even use your existing [MKAnnotationView](https://developer.apple.com/documentation/mapkit/mkannotationview) implementations! -🖼 Overlays +### 🖼 Overlays - Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items - Or: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects -🛠 Appearance / Behavior Customization +### 🛠 Appearance / Behavior Customization - Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype)) - User tracking mode ([MKUserTrackingMode](https://developer.apple.com/documentation/mapkit/mkusertrackingmode)) - Interaction modes (rotation, pitch, zoom and pan) - Point of interest filter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)). -👀 Adapt visibility of: +### 👀 Adapt visibility of: - Buildings - Compass - Pitch control @@ -28,7 +28,7 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati - User location - Zoom controls -🪄 Custom controls +### 🪄 Custom controls - `MapCompass` for [MKCompassButton](https://developer.apple.com/documentation/mapkit/mkcompass) - `MapPitchControl` for [MKPitchControl](https://developer.apple.com/documentation/mapkit/mkpitchcontrol) - `MapScale` for [MKScaleView](https://developer.apple.com/documentation/mapkit/mkscaleview) @@ -36,10 +36,11 @@ MapKit's SwiftUI implementation of [Map](https://developer.apple.com/documentati ## 💻 Supported Platforms -- iOS 13+ -- macOS 10.15+ -- tvOS 13+ -- watchOS 6+ +| 📱 | iOS 13+ | +| :-: | :-: | +| 🖥 | **macOS 10.15+** | +| 📺 | **tvOS 13+** | +| ⌚️ | **watchOS 6+** | Keep in mind that not all features are equally available on all platforms (based on what MapKit provides) and therefore might not be available here either. However, if you can use them using UIKit, there is a very high change that it is available here as well - if not: Let me/us know by creating an issue! From 3fe39464c9f8ba5cb3f5bd2b7b3ca679e00a2521 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 22:22:41 +0200 Subject: [PATCH 07/24] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5715a1b..94e297f 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,10 @@ Map( ) ``` +## Installation + +Map is currently only available via Swift Package Manager. See [this tutorial by Apple](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) on how to add a package dependency to your Xcode project. + ## ✍️ Author Paul Kraft From f354157e996c23da7ac93c6416f754b13c4a7fa1 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Apr 2022 22:24:51 +0200 Subject: [PATCH 08/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94e297f..8d34942 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ Map( ) ``` -## Installation +## 🔩 Installation Map is currently only available via Swift Package Manager. See [this tutorial by Apple](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) on how to add a package dependency to your Xcode project. From 3b615b5abf1c28c8e9a3c9e95ad3ec0599bc0bfb Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:01:15 +0200 Subject: [PATCH 09/24] Make sure not to override region, if it is currently updating --- Sources/Map/Map+Coordinator.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index c43a438..3310c30 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -28,6 +28,7 @@ extension Map { private var registeredAnnotationTypes = Set() private var regionIsChanging = false + private var isUpdating = false // MARK: Initialization @@ -40,7 +41,11 @@ extension Map { // MARK: Methods func update(_ mapView: MKMapView, from newView: Map, context: Context) { - defer { view = newView } + defer { + view = newView + isUpdating = false + } + isUpdating = true let animation = context.transaction.animation updateAnnotations(on: mapView, from: view, to: newView) updateInteractionModes(on: mapView, from: view, to: newView) @@ -228,10 +233,12 @@ extension Map { // MARK: MKMapViewDelegate public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { - DispatchQueue.main.async { [weak self] in - self?.view?.coordinateRegion = mapView.region - self?.view?.mapRect = mapView.visibleMapRect + guard !isUpdating else { + return } + + view?.coordinateRegion = mapView.region + view?.mapRect = mapView.visibleMapRect } @available(macOS 11, *) From d4f9861ce1858a6b3ce0d8e97a8ed52680016bda Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:05:56 +0200 Subject: [PATCH 10/24] Improve region updates --- Sources/Map/Map+Coordinator.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index 3310c30..ccc9fcb 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -194,7 +194,7 @@ extension Map { if newView.usesRegion { let currentRegion = mapView.region - let newRegion = mapView.regionThatFits(newView.coordinateRegion) + let newRegion = newView.coordinateRegion if newRegion.center.latitude != currentRegion.center.latitude || newRegion.center.longitude != currentRegion.center.longitude || newRegion.span.latitudeDelta != currentRegion.span.latitudeDelta @@ -208,8 +208,7 @@ extension Map { || visibleMapRect.origin.y != newRect.origin.y || visibleMapRect.height != newRect.height || visibleMapRect.width != newRect.width { - let newRectThatFits = mapView.mapRectThatFits(newRect) - mapView.setVisibleMapRect(newRectThatFits, animated: animated) + mapView.setVisibleMapRect(newRect, animated: animated) } } @@ -233,9 +232,7 @@ extension Map { // MARK: MKMapViewDelegate public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { - guard !isUpdating else { - return - } + guard !isUpdating else { return } view?.coordinateRegion = mapView.region view?.mapRect = mapView.visibleMapRect From 6d1bd5e276157df8865c0fc19e122f7f5a00a037 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:08:45 +0200 Subject: [PATCH 11/24] Only set region after laying out --- Sources/Map/Map+Coordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index ccc9fcb..85aa842 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -188,7 +188,7 @@ extension Map { } private func updateRegion(on mapView: MKMapView, from previousView: Map?, to newView: Map, animated: Bool) { - guard !regionIsChanging else { + guard !regionIsChanging && !mapView.needsLayout else { return } From 37a722c2686266a3e4a1e86b40b1bb07df694b55 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:11:33 +0200 Subject: [PATCH 12/24] Only set region after display of the map --- Sources/Map/Map+Coordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index 85aa842..af39e5c 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -188,7 +188,7 @@ extension Map { } private func updateRegion(on mapView: MKMapView, from previousView: Map?, to newView: Map, animated: Bool) { - guard !regionIsChanging && !mapView.needsLayout else { + guard !regionIsChanging && !mapView.needsLayout && !mapView.needsDisplay else { return } From 03ee95ff8a7e923a1348d8c01b1c001561b276b1 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 17:13:30 +0200 Subject: [PATCH 13/24] Set region/mapRect asynchronously --- Sources/Map/Map+Coordinator.swift | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index af39e5c..46f0b5f 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -41,11 +41,7 @@ extension Map { // MARK: Methods func update(_ mapView: MKMapView, from newView: Map, context: Context) { - defer { - view = newView - isUpdating = false - } - isUpdating = true + defer { view = newView } let animation = context.transaction.animation updateAnnotations(on: mapView, from: view, to: newView) updateInteractionModes(on: mapView, from: view, to: newView) @@ -188,7 +184,7 @@ extension Map { } private func updateRegion(on mapView: MKMapView, from previousView: Map?, to newView: Map, animated: Bool) { - guard !regionIsChanging && !mapView.needsLayout && !mapView.needsDisplay else { + guard !regionIsChanging else { return } @@ -199,7 +195,9 @@ extension Map { || newRegion.center.longitude != currentRegion.center.longitude || newRegion.span.latitudeDelta != currentRegion.span.latitudeDelta || newRegion.span.longitudeDelta != currentRegion.span.longitudeDelta { - mapView.setRegion(newRegion, animated: animated) + DispatchQueue.main.async { + mapView.setRegion(newRegion, animated: animated) + } } } else { let visibleMapRect = mapView.visibleMapRect @@ -208,7 +206,9 @@ extension Map { || visibleMapRect.origin.y != newRect.origin.y || visibleMapRect.height != newRect.height || visibleMapRect.width != newRect.width { - mapView.setVisibleMapRect(newRect, animated: animated) + DispatchQueue.main.async { + mapView.setVisibleMapRect(newRect, animated: animated) + } } } @@ -232,8 +232,6 @@ extension Map { // MARK: MKMapViewDelegate public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { - guard !isUpdating else { return } - view?.coordinateRegion = mapView.region view?.mapRect = mapView.visibleMapRect } From 984bd3484908fbadc7be2d683d3ae02e8c4ba853 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Fri, 29 Apr 2022 19:28:06 +0200 Subject: [PATCH 14/24] Add MapKit equality methods --- Sources/Extensions/MapKit.swift | 61 +++++++++++++++++++++++++++++++ Sources/Map/Map+Coordinator.swift | 28 +++++--------- 2 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 Sources/Extensions/MapKit.swift diff --git a/Sources/Extensions/MapKit.swift b/Sources/Extensions/MapKit.swift new file mode 100644 index 0000000..52ad423 --- /dev/null +++ b/Sources/Extensions/MapKit.swift @@ -0,0 +1,61 @@ +// +// MapKit.swift +// Map +// +// Created by Paul Kraft on 29.04.22. +// + +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 + } + +} + +extension MKMapPoint { + + func equals(to other: MKMapPoint) -> Bool { + x == other.x + && y == other.y + } + +} +extension MKMapRect { + + func equals(to other: MKMapRect) -> Bool { + origin.equals(to: other.origin) + && size.equals(to: other.size) + } + +} + +extension MKMapSize { + + func equals(to other: MKMapSize) -> Bool { + width == other.width + && height == other.height + } + +} diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index 46f0b5f..e39fe8a 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -28,7 +28,6 @@ extension Map { private var registeredAnnotationTypes = Set() private var regionIsChanging = false - private var isUpdating = false // MARK: Initialization @@ -189,28 +188,21 @@ extension Map { } if newView.usesRegion { - let currentRegion = mapView.region let newRegion = newView.coordinateRegion - if newRegion.center.latitude != currentRegion.center.latitude - || newRegion.center.longitude != currentRegion.center.longitude - || newRegion.span.latitudeDelta != currentRegion.span.latitudeDelta - || newRegion.span.longitudeDelta != currentRegion.span.longitudeDelta { - DispatchQueue.main.async { - mapView.setRegion(newRegion, animated: animated) - } + guard !mapView.region.equals(to: newRegion) else { + return + } + DispatchQueue.main.async { + mapView.setRegion(newRegion, animated: animated) } } else { - let visibleMapRect = mapView.visibleMapRect let newRect = newView.mapRect - if visibleMapRect.origin.x != newRect.origin.x - || visibleMapRect.origin.y != newRect.origin.y - || visibleMapRect.height != newRect.height - || visibleMapRect.width != newRect.width { - DispatchQueue.main.async { - mapView.setVisibleMapRect(newRect, animated: animated) - } + guard !mapView.visibleMapRect.equals(to: newRect) else { + return + } + DispatchQueue.main.async { + mapView.setVisibleMapRect(newRect, animated: animated) } - } } From 891d52598bbdd032305c126605d26a1e62b35fac Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 30 Apr 2022 00:48:34 +0200 Subject: [PATCH 15/24] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d34942..15d13b0 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,6 @@ For the use of `MapCompass`, `MapPitchControl`, `MapScale` and `MapZoomControl` Example: We want to display a scale overlay at the topLeading edge of a `Map`. To accomplish this, let's take a look at the following code snippet. ```swift -struct MyMapKey: Hashable {} struct MyMapView: View { @@ -202,9 +201,9 @@ struct MyMapView: View { var body: some View { Map(coordinateRegion: $region) - .mapKey(MyMapKey()) + .mapKey(1) .overlay(alignment: .topLeading) { - MapScale(key: MyMapKey(), alignment: .leading, visibility: .visible) + MapScale(key: 1, alignment: .leading, visibility: .visible) .fixedSize() .padding(12) } From 7ac2a0188dafa0c3bdf31eec193606705c6a7d9c Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 30 Apr 2022 00:48:48 +0200 Subject: [PATCH 16/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15d13b0..217b3ee 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ Map( annotationContent: { item in if { ImageAnnotation(coordinate: item.coordinate, image: UIImage(...), centerOffset: CGPoint(x: 0, y: -2) - } else if { + } else { MapPin(coordinate: item.coordinate, color: .red) // color can only be red, green or purple } } From 67cb9081ae82d070c21db3a66c2cf19f1ef07b1b Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 5 May 2022 21:38:36 +0200 Subject: [PATCH 17/24] Create Map.podspec (#10) --- Map.podspec | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Map.podspec diff --git a/Map.podspec b/Map.podspec new file mode 100644 index 0000000..6f0bf5c --- /dev/null +++ b/Map.podspec @@ -0,0 +1,26 @@ +Pod::Spec.new do |spec| + + spec.name = 'Map' + spec.version = '0.1.0' + spec.license = { :type => 'MIT' } + spec.homepage = 'https://github.com/pauljohanneskraft/Map' + spec.authors = { 'Paul Kraft' => 'pauljohanneskraft@users.noreply.github.com' } + spec.summary = 'MKMapView wrapper for SwiftUI as drop-in to MapKit\'s SwiftUI view. Easily extensible annotations and overlays, iOS 13 support and backwards compatible with MKAnnotation and MKOverlay!' + spec.source = { :git => 'https://github.com/pauljohanneskraft/Map', :tag => '0.1.0' } + spec.module_name = 'Map' + spec.swift_version = '5.1' + + spec.ios.deployment_target = '13.0' + spec.osx.deployment_target = '10.15' + spec.tvos.deployment_target = '13.0' + spec.watchos.deployment_target = '6.0' + + spec.source_files = 'Sources/**/*.swift' + + spec.framework = 'SwiftUI' + spec.ios.framework = 'UIKit' + spec.osx.framework = 'AppKit' + spec.tvos.framework = 'UIKit' + spec.watchos.framework = 'WatchKit' + +end From daa42c42d6f2e9b93d372db378733c51b9b5bbf9 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Thu, 5 May 2022 21:46:31 +0200 Subject: [PATCH 18/24] Update Pod version to 0.1.1 --- Map.podspec | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Map.podspec b/Map.podspec index 6f0bf5c..d159d00 100644 --- a/Map.podspec +++ b/Map.podspec @@ -1,12 +1,15 @@ Pod::Spec.new do |spec| spec.name = 'Map' - spec.version = '0.1.0' + spec.version = '0.1.1' spec.license = { :type => 'MIT' } spec.homepage = 'https://github.com/pauljohanneskraft/Map' spec.authors = { 'Paul Kraft' => 'pauljohanneskraft@users.noreply.github.com' } - spec.summary = 'MKMapView wrapper for SwiftUI as drop-in to MapKit\'s SwiftUI view. Easily extensible annotations and overlays, iOS 13 support and backwards compatible with MKAnnotation and MKOverlay!' - spec.source = { :git => 'https://github.com/pauljohanneskraft/Map', :tag => '0.1.0' } + spec.summary = 'More capable MKMapView wrapper for SwiftUI as drop-in to MapKit\'s SwiftUI view.' + spec.description = 'MKMapView wrapper for SwiftUI as drop-in to MapKit\'s SwiftUI view. Easily extensible annotations and overlays, iOS 13 support +and backwards compatible with MKAnnotation and MKOverlay!' + spec.source = { :git => +'https://github.com/pauljohanneskraft/Map.git', :tag => '0.1.1' } spec.module_name = 'Map' spec.swift_version = '5.1' From a99a9083aac4c5eba1cd83291eb2e222df287f31 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Thu, 5 May 2022 21:47:56 +0200 Subject: [PATCH 19/24] Set cocoapods version to 0.1.0 --- Map.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Map.podspec b/Map.podspec index d159d00..252c9db 100644 --- a/Map.podspec +++ b/Map.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = 'Map' - spec.version = '0.1.1' + spec.version = '0.1.0' spec.license = { :type => 'MIT' } spec.homepage = 'https://github.com/pauljohanneskraft/Map' spec.authors = { 'Paul Kraft' => 'pauljohanneskraft@users.noreply.github.com' } @@ -9,7 +9,7 @@ Pod::Spec.new do |spec| spec.description = 'MKMapView wrapper for SwiftUI as drop-in to MapKit\'s SwiftUI view. Easily extensible annotations and overlays, iOS 13 support and backwards compatible with MKAnnotation and MKOverlay!' spec.source = { :git => -'https://github.com/pauljohanneskraft/Map.git', :tag => '0.1.1' } +'https://github.com/pauljohanneskraft/Map.git', :tag => '0.1.0' } spec.module_name = 'Map' spec.swift_version = '5.1' From 35e35249f73d622378e8218eeb43db8851ead1bb Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Tue, 14 Jun 2022 20:12:07 +0200 Subject: [PATCH 20/24] Make sure information visibility is correctly updated --- Sources/Map/Map+Coordinator.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index e39fe8a..f283676 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -43,6 +43,7 @@ extension Map { defer { view = newView } let animation = context.transaction.animation updateAnnotations(on: mapView, from: view, to: newView) + updateInformationVisibility(on: mapView, from: view, to: newView) updateInteractionModes(on: mapView, from: view, to: newView) updateOverlays(on: mapView, from: view, to: newView) updatePointOfInterestFilter(on: mapView, from: view, to: newView) From 2b14e2a13b2ef68b9659ceba34891aee73800f91 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Tue, 14 Jun 2022 20:14:05 +0200 Subject: [PATCH 21/24] Ignore unknown annotation types --- Sources/Map/Map+Coordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index f283676..a5cff01 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -268,7 +268,7 @@ extension Map { public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard let content = annotationContentByObject[ObjectIdentifier(annotation)] else { - assertionFailure("Somehow an unknown annotation appeared.") + print("Unknown annotation appeared: \(annotation).") return nil } return content.view(for: mapView) From 26e42fd53f9dbfd329c4cc2b66a8fa41c9e70725 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Tue, 14 Jun 2022 22:23:52 +0200 Subject: [PATCH 22/24] Do not report unknown annotations anymore --- Sources/Map/Map+Coordinator.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index a5cff01..1fb2439 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -268,7 +268,6 @@ extension Map { public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard let content = annotationContentByObject[ObjectIdentifier(annotation)] else { - print("Unknown annotation appeared: \(annotation).") return nil } return content.view(for: mapView) From 0cc4ad0acd5212029c79ea0f481880d4a17af1f3 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Thu, 16 Jun 2022 21:17:33 +0200 Subject: [PATCH 23/24] Update region asynchronously --- Sources/Map/Map+Coordinator.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index 1fb2439..fb1c8d4 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -225,8 +225,10 @@ extension Map { // MARK: MKMapViewDelegate public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { - view?.coordinateRegion = mapView.region - view?.mapRect = mapView.visibleMapRect + Task { @MainActor [weak self] in + self?.view?.coordinateRegion = mapView.region + self?.view?.mapRect = mapView.visibleMapRect + } } @available(macOS 11, *) From 4f9a37bc3a2254064d740f9549f9a09690e3e790 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Thu, 16 Jun 2022 21:19:30 +0200 Subject: [PATCH 24/24] Make sure to update region asynchronously --- Sources/Map/Map+Coordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Map/Map+Coordinator.swift b/Sources/Map/Map+Coordinator.swift index fb1c8d4..a6c52e0 100644 --- a/Sources/Map/Map+Coordinator.swift +++ b/Sources/Map/Map+Coordinator.swift @@ -225,7 +225,7 @@ extension Map { // MARK: MKMapViewDelegate public func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { - Task { @MainActor [weak self] in + DispatchQueue.main.async { [weak self] in self?.view?.coordinateRegion = mapView.region self?.view?.mapRect = mapView.visibleMapRect }