{"id":16957269,"url":"https://github.com/pauljohanneskraft/map","last_synced_at":"2025-04-09T23:18:48.910Z","repository":{"id":37943428,"uuid":"485377485","full_name":"pauljohanneskraft/Map","owner":"pauljohanneskraft","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!","archived":false,"fork":false,"pushed_at":"2023-10-02T17:56:21.000Z","size":85,"stargazers_count":196,"open_issues_count":22,"forks_count":49,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-04-09T23:18:42.408Z","etag":null,"topics":["core-location","corelocation","ios","maccatalyst","macos","map","mapkit","mkannotation","mkmapview","mkoverlay","swift","swift-package-manager","swiftpackagemanager","swiftui","tvos","watchos"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pauljohanneskraft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-25T13:12:52.000Z","updated_at":"2025-03-31T17:41:10.000Z","dependencies_parsed_at":"2024-10-26T21:19:42.036Z","dependency_job_id":"b7e984d0-2afd-4824-96ff-46ee9bdf4214","html_url":"https://github.com/pauljohanneskraft/Map","commit_stats":{"total_commits":58,"total_committers":2,"mean_commits":29.0,"dds":"0.017241379310344862","last_synced_commit":"108d41379e7aa3f1645ce1174d73564c250c9137"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pauljohanneskraft%2FMap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pauljohanneskraft%2FMap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pauljohanneskraft%2FMap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pauljohanneskraft%2FMap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pauljohanneskraft","download_url":"https://codeload.github.com/pauljohanneskraft/Map/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125592,"owners_count":21051771,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["core-location","corelocation","ios","maccatalyst","macos","map","mapkit","mkannotation","mkmapview","mkoverlay","swift","swift-package-manager","swiftpackagemanager","swiftui","tvos","watchos"],"created_at":"2024-10-13T22:18:36.743Z","updated_at":"2025-04-09T23:18:48.874Z","avatar_url":"https://github.com/pauljohanneskraft.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Map](https://user-images.githubusercontent.com/15239005/165400895-182eb850-f05a-4aa5-b525-866efd5628c5.png)\n\nMapKit'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:\n\n## 🚀 Features\n\n### 📍 Annotations\n- Create annotations from annotationItems as in the default MapKit SwiftUI implementation.\n- 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!\n\n### 🖼 Overlays\n- Use a SwiftUI-style API based on `Identifiable` with overlay items and a closure to create overlays from these items\n- Or: Use existing [MKOverlay](https://developer.apple.com/documentation/mapkit/mkoverlay) / [MKOverlayRenderer](https://developer.apple.com/documentation/mapkit/mkoverlayrenderer) objects\n\n### 🛠 Appearance / Behavior Customization\n- Map type ([MKMapType](https://developer.apple.com/documentation/mapkit/mkmaptype))\n- User tracking mode ([MKUserTrackingMode](https://developer.apple.com/documentation/mapkit/mkusertrackingmode))\n- Interaction modes (rotation, pitch, zoom and pan)\n- Point of interest filter ([MKPointOfInterestFilter](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter)).\n\n### 👀 Adapt visibility of:\n- Buildings\n- Compass\n- Pitch control\n- Scale\n- Traffic\n- User heading\n- User location\n- Zoom controls\n\n### 🪄 Custom controls\n- `MapCompass` for [MKCompassButton](https://developer.apple.com/documentation/mapkit/mkcompass)\n- `MapPitchControl` for [MKPitchControl](https://developer.apple.com/documentation/mapkit/mkpitchcontrol)\n- `MapScale` for [MKScaleView](https://developer.apple.com/documentation/mapkit/mkscaleview)\n- `MapZoomControl` for [MKZoomControl](https://developer.apple.com/documentation/mapkit/mkzoomcontrol)\n\n## 💻 Supported Platforms\n\n| 📱 | iOS 13+ |\n| :-: | :-: |\n| 🖥 | **macOS 10.15+** | \n| 📺 | **tvOS 13+** |\n| ⌚️ | **watchOS 6+** |\n\nKeep 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!\n\n## 🧑🏽‍💻 Usage on iOS, macOS and tvOS\n\nVery 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.\n\n```swift\nimport Map\nimport SwiftUI\n\nstruct MyMapView: View {\n\n    let locations: [MyLocation]\n    let directions: MKDirections.Response\n    \n    @State private var region = MKCoordinateRegion()\n    @State private var userTrackingMode = UserTrackingMode.follow\n\n    var body: some View {\n        Map(\n          coordinateRegion: $region,\n          type: .satelliteFlyover,\n          pointOfInterestFilter: .excludingAll,\n          informationVisibility: .default.union(.userLocation),\n          interactionModes: [.pan, .rotate],\n          userTrackingMode: $userTrackingMode,\n          annotationItems: locations,\n          annotationContent: { location in\n              ViewMapAnnotation(coordinate: location.coordinate) {\n                  Color.red\n                    .frame(width: 24, height: 24)\n                    .clipShape(Circle())\n              }\n          },\n          overlays: directions.routes.map { $0.polyline },\n          overlayContent: { overlay in\n              RendererMapOverlay(overlay: overlay) { _, overlay in\n                  if let polyline = overlay as? MKPolyline else {\n                      let isFirstRoute = overlay === directions.routes.first?.overlay\n                      let renderer = MKPolylineRenderer(polyline: polyline)\n                      renderer.lineWidth = 6\n                      renderer.strokeColor = isFirstRoute ? .systemBlue : .systemGray\n                      return renderer\n                  } else {\n                      assertionFailure(\"Unknown overlay type found.\")\n                      return MKOverlayRenderer(overlay: overlay)\n                  }\n              }\n          }\n        )\n        .onAppear {\n            region = // ...\n        }\n    }\n\n}\n```\n\n### 📍 Annotations: The modern approach\n\nYou 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`).\n\n```swift\nMap(\n    coordinateRegion: $region,\n    annotationItems: items,\n    annotationContent: { item in\n        if \u003cfirst condition\u003e {\n            ViewMapAnnotation(coordinate: location.coordinate) {\n                Color.red\n                    .frame(width: 24, height: 24)\n                    .clipShape(Circle())\n             }\n         else if \u003csecond condition\u003e {\n             MapMarker(coordinate: item.coordinate, tint: .red) // tint is `UIColor`, `NSColor` or `Color`\n         } else {\n             MapPin(coordinate: item.coordinate, tint: .blue) // tint is `UIColor`, `NSColor` or `Color`\n         }\n     }\n)\n```\n\n### 📌 Annotations: The old-fashioned approach\n\nMoving 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.\n\n```swift\npublic protocol MapAnnotation {\n\n    static func registerView(on mapView: MKMapView)\n    \n    var annotation: MKAnnotation { get }\n\n    func view(for mapView: MKMapView) -\u003e MKAnnotationView?\n    \n}\n```\n\nIn `registerView(on:)`, your custom annotation implementation can register a cell type for dequeuing using [`MKMapView.register(_:forAnnotationViewWithReuseIdentifier:)`](https://developer.apple.com/documentation/mapkit/mkmapview/2887124-register). To dequeue the registered cell, implement the `view(for:)` method, similar to [`MKMapViewDelegate.mapView(_:viewFor:)`](https://developer.apple.com/documentation/mapkit/mkmapviewdelegate/1452045-mapview).\n\nNote: 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!\n\n### 🌃 Overlays: The modern approach\n\nSimilarly 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:\n\n```swift\npublic protocol MapOverlay {\n\n    var overlay: MKOverlay { get }\n    \n    func renderer(for mapView: MKMapView) -\u003e MKOverlayRenderer\n    \n}\n```\n\nIn your implementation, the `renderer(for:)` method creates a renderer for the overlay, similar to [`MKMapViewDelegate.mapView(_:rendererFor:)`](https://developer.apple.com/documentation/mapkit/mkmapviewdelegate/1452203-mapview).\n\nNote: 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!\n\n### 🖼 Overlays: The old-fashioned approach\n\nEspecially 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).\n\nFor this, we provide `RendererMapOverlay`:\n\n```swift\nMap(\n    coordinateRegion: $region,\n    overlays: directions.routes.map { $0.polyline },\n    overlayContent: { overlay in\n        RendererMapOverlay(overlay: overlay) { mapView, overlay in\n            guard let polyline = overlay as? MKPolyline else {\n                assertionFailure(\"Unknown overlay type encountered.\")\n                return MKMapOverlayRenderer(overlay: overlay)\n            }\n            let renderer = MKPolylineRenderer(polyline: polyline)\n            renderer.lineWidth = 4\n            renderer.strokeColor = .red\n            return renderer\n        }\n    }\n)\n```\n\n### 🪄 Custom Map Controls\n\nFor 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.\n\nExample: 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.\n\n```swift\n\nstruct MyMapView: View {\n\n    @Binding var region: MKCoordinateRegion\n    \n    var body: some View {\n        Map(coordinateRegion: $region)\n            .mapKey(1)\n            .overlay(alignment: .topLeading) {\n                MapScale(key: 1, alignment: .leading, visibility: .visible)\n                    .fixedSize()\n                    .padding(12)\n            }\n    }\n}\n```\n\n## ⌚️ Usage on watchOS\n\nSince 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).\n\nWe do include a drop-in interface though for projects that target multiple platforms and share code extensively across these platforms.\n\n```swift\nMap(\n    coordinateRegion: $region,\n    informationVisibility: [.userHeading, .userLocation],\n    userTrackingMode: $userTrackingMode,\n    annotationItems: annotationItems,\n    annotationContent: { item in\n        if \u003cfirst condition\u003e {\n            ImageAnnotation(coordinate: item.coordinate, image: UIImage(...), centerOffset: CGPoint(x: 0, y: -2) \n        } else {\n            MapPin(coordinate: item.coordinate, color: .red) // color can only be red, green or purple\n        }\n    }\n)\n```\n\n## 🔩 Installation\n\nMap 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.\n\n## ✍️ Author\n\nPaul Kraft\n\n## 📄 License\n\nMap is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpauljohanneskraft%2Fmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpauljohanneskraft%2Fmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpauljohanneskraft%2Fmap/lists"}