Display map or satellite imagery from your app's interface, call out points of interest, and determine placemark information for map coordinates using MapKit.

Posts under MapKit tag

121 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Image using MKLookAroundSnapshotter with Updated Scene
I'm trying to create a UIImage from a MKLookAroundScene, after the user has moved the scene, by looking (and moving) around. Is this possible? When I use MKLookAroundSnapshotter with the modified scene, I always get an image of the original (starting) scene. STEPS TO REPRODUCE Create a scene using MKLookAroundSceneRequest with coordinates. Use LookAroundPreview with a binding to MKLookAroundScene to get changes to the scene (based on looking around). User begins LookAround, moving the scene location and view. User ends LookAround The preview displays the updated scene properly. The scene's cameraFrameOverride values reflect the pitch, roll, yaw, and location changes. Pass the updated scene to MKLookAroundSnapshotter to get an image. Display the Image. The Image is the original scene (not what is displayed in the preview).
1
0
488
Sep ’24
MKScaleView doesn't update changing area programmatically
I have a MKMapView with a MKScaleView. If I visualise a generic map I have the scale in km. When I change the MKMapRect using visibleMapRect, the scale doesn't change. If I use setVisibleMapRect(_ mapRect: MKMapRect, animated animate: Bool), the scale change but not to the correct one. For example, it shows a scale saying one inch corresponds to 250 m while it is 150 m. The same issue of I use MKCoordinateRegion. Instead, if I zoom in or zoom out pinching on the map, the scale updates correctly. Am I doing something wrong? How can I fix this? Sample code: import UIKit import MapKit let CORNER_RADIUS: CGFloat = 8.0 let METERS_PER_MILE: Double = 1609.344 class PIAnnotation: NSObject, MKAnnotation { var coordinate: CLLocationCoordinate2D private(set) var title: String? private(set) var subtitle: String? init(location: CLLocationCoordinate2D, title: String? = nil, subtitle: String? = nil) { coordinate = location self.title = title self.subtitle = subtitle } } class PISimpleMapView: MKMapView { private let HALF_MAP_SIDE_MULTIPLIER: Double = 1.4 private let pinIdentifier = "pinIdentifier" private var scaleView: MKScaleView? typealias PinAnnotationView = MKMarkerAnnotationView // MKPinAnnotationView required init?(coder: NSCoder) { super.init(coder: coder) inizialize() } override init(frame: CGRect) { super.init(frame: frame) inizialize() } func inizialize() { layer.cornerRadius = CORNER_RADIUS register(PinAnnotationView.self, forAnnotationViewWithReuseIdentifier: pinIdentifier) addScale() } private func addScale() { let scale = MKScaleView(mapView: self) scale.translatesAutoresizingMaskIntoConstraints = false scale.scaleVisibility = .visible // always visible addSubview(scale) let guide = safeAreaLayoutGuide NSLayoutConstraint.activate([ scale.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16.0), scale.rightAnchor.constraint(equalTo: guide.centerXAnchor), scale.topAnchor.constraint(equalTo: guide.topAnchor), scale.heightAnchor.constraint(equalToConstant: 20.0) ]) scaleView?.removeFromSuperview() scaleView = scale } func displayPinOnMap(location: CLLocation) { let annotation = PIAnnotation(location: location.coordinate, title: "Sample", subtitle: nil) addAnnotation(annotation) // Position the map so that all overlays and annotations are visible on screen. visibleMapRect = visibleArea(from: annotation) // setVisibleMapRect(visibleArea(from: annotation), animated: true) // region = MKCoordinateRegion(visibleArea(from: annotation)) } private func visibleArea(from annotation: PIAnnotation) -> MKMapRect { let annotationPoint = MKMapPoint(annotation.coordinate) return MKMapRect(x: annotationPoint.x - HALF_MAP_SIDE_MULTIPLIER * METERS_PER_MILE, y: annotationPoint.y - HALF_MAP_SIDE_MULTIPLIER * METERS_PER_MILE, width: HALF_MAP_SIDE_MULTIPLIER * 2.0 * METERS_PER_MILE, height: HALF_MAP_SIDE_MULTIPLIER * 2.0 * METERS_PER_MILE) } }
5
0
681
Sep ’24
SwiftUI and MapKit - Find address from coordinates
I've seen a lot of posts around the internet about getting coordinates from an address, but none about how to do the opposite. What I would like to do is allow a person to drop a pin and then programmatically look up the address closest to the pin. Like if I drop a pin inside a mall on the map I would like to capture the address to the mall. Does anyone know how this could be done?
2
0
1.1k
Aug ’24
LongPressGesture to add and select Map Pin
Hi there! I am tying to add and select a map pin using view long press when the gesture is begin. However after adding and selecting the pin it got deselected immediately, my assumption that it's conflicting when the original map gesture and it's deselecting the pin on the map (like if a pin was selected and you click anywhere on the map to deselect it). Note that if I increase minimumPressDuration for the UILongPressGestureRecognizer it works fine, but I want the value at 0.5 so the pin is quickly added. here is the full code: import SwiftUI import MapKit struct ContentView: View { @State var selection: CustomMapSelection? @State private var longPressPosition: CGPoint = .zero @State private var pinCoordinate: CLLocationCoordinate2D? var body: some View { MapReader { proxy in Map(selection: $selection) { if let pinCoordinate { Marker("Custom Location", coordinate: pinCoordinate) .tag(CustomMapSelection(coordinate: pinCoordinate)) } } .gesture(LongPressGestureRecognizer(position: $longPressPosition)) .onChange(of: longPressPosition) { if let coordinate = proxy.convert(longPressPosition, from: .global) { pinCoordinate = coordinate selection = CustomMapSelection(coordinate: coordinate) } } } } } struct CustomMapSelection: Hashable { let latitude: Double let longitude: Double init(coordinate: CLLocationCoordinate2D) { latitude = coordinate.latitude longitude = coordinate.longitude } } struct LongPressGestureRecognizer: UIGestureRecognizerRepresentable { @Binding var position: CGPoint func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator { Coordinator() } func makeUIGestureRecognizer(context: Context) -> UILongPressGestureRecognizer { let recognizer = UILongPressGestureRecognizer() recognizer.delegate = context.coordinator // if you make the minimumPressDuration above 2, it works fine and the selection doesn't cancel recognizer.minimumPressDuration = 0.5 return recognizer } func handleUIGestureRecognizerAction(_ recognizer: UILongPressGestureRecognizer, context: Context) { if recognizer.state == .began { let position = recognizer.location(in: recognizer.view) self.position = position } } class Coordinator: NSObject, UIGestureRecognizerDelegate { func gestureRecognizer( _ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer ) -> Bool { return true } } } #Preview { ContentView() }
1
0
728
Aug ’24
Loading large amounts of coordinates and annotations to a map?
My query might return 20,000 coordinates. Does MapKit JS try to load all the coordinates at once into the map or does it only load what’s in the viewport of the map if we were to load 20,000 annotations into the say something like “landmark data” const? We have 900,000 coordinates to load into Los Angeles and are planning how we will do this. Obviously we can’t load 900,000 coordinates at once without performance issues, but some query’s return 20,000 results. Can someone point me to some information about large datasets and MapKit js or let me know if it’s handled and already built in to not try to load that many locations at once?
0
0
576
Aug ’24
Is it possible to display a live 3D view of Google Maps in a Vision Pro app?
Hi everyone, I’m exploring the idea of displaying a live 3D view of Google Maps in a Vision Pro app using SwiftUI and RealityKit. I want users to be able to interact with the map, including panning, zooming in and out, and exploring different areas in a fully immersive environment. Is this technically possible within the Vision Pro ecosystem? If so, what would be the recommended approach to implement this? If not, are there any alternative methods or platforms that could provide a similar experience? Thanks in advance for your insights! Best, Siddharth
1
0
1.3k
Aug ’24
Swift 6 Concurrency Errors with MKLocalSearchCompleterDelegate results
Has anyone found a thread-safe pattern that can extract results from completerDidUpdateResults(MKLocalSearchCompleter) in the MKLocalSearchCompleterDelegate ? I've downloaded the code sample from Interacting with nearby points of interest and notice the conformance throws multiple errors in Xcode 16 Beta 5 with Swift 6: extension SearchDataSource: MKLocalSearchCompleterDelegate { nonisolated func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) { Task { let suggestedCompletions = completer.results await resultStreamContinuation?.yield(suggestedCompletions) } } Error: Task-isolated value of type '() async -> ()' passed as a strongly transferred parameter; later accesses could race and Error: Sending 'suggestedCompletions' risks causing data races Is there another technique I can use to share state of suggestedCompletions outside of the delegate in the code sample?
4
2
2.1k
Dec ’24
SwiftUI Map + Marker + AsyncImage Support
I would like to be able to use AsyncImage inside of a SwiftUI Map Marker like this: Map(position: $cameraPosition) { Marker(coordinate: coord) { AsyncImage(url: url) } } But when I try to do this I get one of two outcomes. The first outcome I've seen is a crash like this: Thread 1: EXC_BAD_ACCESS (code=1, address=0x20) The second possible outcome I've seen is that the image doesn't load but just has a blank placeholder like this: I also tried creating a custom View that takes the url, downloads the data asynchronously, converts the data to a UIImage, sets the UIImage to a @State variable and then uses the Image(uiimage:) SwiftUI view but I got the same results. Has anyone else gotten something like this working?
2
0
556
Aug ’24
In MKMapView, show (some) annotations only when zoomed in
I have a UIKit app with an MKMapview. In that mapview, I show icons on the location of Airfields. When zooming out to Europe (or USA for that matter), the whole map is covered with the annotations, so I want to only show these annotations when zoomed in beyond some level. How can that be achieved? I did find a way like this: class MapViewController: UIViewController { var isAtBigZoom = true { didSet { guard oldValue != isAtBigZoom else { return } for case let annot in mapView.annotations { mapView.view(for: annot)?.alpha = isAtBigZoom ? 1 : 0 } } } } extension MapViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) { isAtBigZoom = mapView.region.span.latitudeDelta < self.airportThreshold } } But I have 2 problems with that: Seems like a lot of processing power It only takes effect after a pan. So I zoom beyond the limit, alpha has the 'old' value. Only after I pan, the alpha is suddenly represented in the MapView. Does anybody know a better solution?
3
0
904
Aug ’24
Showing MKPolygon cause termination of app (iOS 18.0 public beta)
The code worked on previous iOS versions (16 and 17). Recently I updated an iPhone to iOS 18.0 public beta and now the code of presenting a route fails with fatal error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MKPolygon needsElevationCorrection]: unrecognized selector sent to instance 0x303295cc0' *** First throw call stack: (0x18861f11c 0x18591e698 0x1887247d4 0x1885bc888 0x1885bc1b0 0x191b08f28 0x191b09150 0x1a86bd424 0x1a86be134 0x191a69344 0x1919d68b8 0x1919d4e90 0x1919d5078 0x1919d54b0 0x1919d59a8 0x1919cb31c 0x104655110 0x104497ca9 0x104498309 0x1043e555d 0x1043e9eb9 0x194084689) libc++abi: terminating due to uncaught exception of type NSException The code that works on previous versions: final class MapService: NSObject, MapServiceInterface, ObservableObject { var mapView = MKMapView() override init() { super.init() mapView.delegate = self setup() } func setRoute(coordinates: [CLLocationCoordinate2D]) async { let polyline = MKPolygon(coordinates: coordinates, count: coordinates.count) await mapView.addOverlay(polyline) // fails and terminates app here let edges = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 12.0, right: 10.0) await mapView.setVisibleMapRect( polyline.boundingMapRect, edgePadding: edges, animated: true) } func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = MKPolylineRenderer(overlay: overlay) renderer.strokeColor = UIColor(.modernBlue300) renderer.lineWidth = 6 return renderer } } The view: struct MapView: View { @StateObject var viewModel = MapViewModel() var body: some View { Wrapper(view: viewModel.mapService.mapView) } }
1
0
430
Aug ’24
MKLocalSearch request limit
I'm a bit unsure about whether there’s a limit on the number of requests when using MKLocalSearch from MapKit. For example, if I have a large user base and need to make 200 requests per minute at peak times, will MapKit be able to handle this? Will the user be throttled after a number of requests? If there is a limit, is it based on the user or developer account? Additionally, if there is a limit, is it per day? Can you clarify? Just to give you an idea, here is an example of the request we make: let searchRequest = MKLocalSearch.Request() searchRequest.naturalLanguageQuery = myQueryStringHere localSearch?.cancel() // cancel the previous call if it exists localSearch = MKLocalSearch(request: searchRequest) localSearch?.start { (response, error) in guard error == nil else { completion(.failure(.myError)) return } let mapSearchLocations = response?.mapItems completion(.success(mapSearchLocations)) } } I appreciate your help in advance.
1
0
625
Aug ’24
MKLocalSearch request limit
Hello, It’s unclear to me if there is a limit of requests when using MKLocalSearch from MapKit. Let’s say I have a very large user base and will use 1000 requests per minute at peak times. Will MapKit support this? But if there is a limit, is it by user or by developer account? Also, if there is a limit, is it per day? Here is an example of the request we use. let searchRequest = MKLocalSearch.Request() searchRequest.naturalLanguageQuery = myQueryStringHere localSearch?.cancel() // cancel the previous call if it exists localSearch = MKLocalSearch(request: searchRequest) localSearch?.start { (response, error) in guard error == nil else { completion(.failure(.myError)) return } let mapSearchLocations = response?.mapItems completion(.success(mapSearchLocations)) } }
1
0
497
Aug ’24
MapKit .standard doesn't allow for globe view when zoomed out
Hi, I'm writing because I've been trying to set the .standard map style to render as a globe on an iPhone 15 running iOS 17 when zoomed out similar to how it works in Apple Maps but can't find any resources on how to make that happen as every time it appears as a flat 2D map, much unlike the .hybrid and .satellite types. I found another thread on this forum a year ago where someone said .standard was bugged and couldn't become a globe, but I'm honestly quite surprised this issue hasn't been brought up more often.
3
2
789
Jul ’24
pointOfInterestCategory missing on mapkit js placeLookup
const lookup = new mapkit.PlaceLookup() lookup.getPlace(input?.id, (error, place) => { console.log("place", place) ... gives me for example { "id": "I65A54A72CE9E45D6", "alternateIds": [ "IB86C41DA005E0D9B" ], "muid": "7324342225941186006", "_styleAttributes": "4:226,6:16,10:0,82:12,85:12,89:1,164:1,193:1", "name": "The Museum of Modern Art", "region": { "center": { "latitude": 40.7612829, "longitude": -73.9768677 }, "span": { "latitudeDelta": 0.008983199999995861, "longitudeDelta": 0.01186000000001286 } }, "coordinate": { "latitude": 40.7617238, "longitude": -73.9777654 }, "formattedAddress": "11 W 53rd St, New York, NY 10019, United States", "countryCode": "US", "telephone": "+12127089400", "urls": [ "http://www.moma.org" ], "country": "United States", "administrativeArea": "New York", "administrativeAreaCode": "NY", "locality": "New York", "postCode": "10019", "subLocality": "Manhattan", "thoroughfare": "W 53rd St", "subThoroughfare": "11", "fullThoroughfare": "11 W 53rd St", "areasOfInterest": [ "Manhattan" ], "dependentLocalities": [ "Midtown Center", "Midtown East", "Midtown Manhattan", "Midtown", "North Hudson" ], "timezone": "America/New_York", "timezoneSecondsFromGmt": -14400 } Note there is no pointOfInterestCategory. but const place_search = new mapkit.Search() place_search.search( input, (error, result) => { console.log("result_places_0", result?.places?.[0]) ... i get { "id": "I65A54A72CE9E45D6", "alternateIds": [ "IB86C41DA005E0D9B" ], "muid": "7324342225941186006", "_wpURL": "https://maps.apple.com/place?q=The%20Museum%20of%20Modern%20Art&auid=7324342225941186006&address=11%20W%2053rd%20St,%20New%20York,%20NY%20%2010019,%20United%20States&ll=40.7617238,-73.9777654", "_styleAttributes": "4:226,6:16,10:0,82:12,85:12,89:1,164:1,193:1", "pointOfInterestCategory": "Museum", "name": "The Museum of Modern Art", "region": { "center": { "latitude": 40.7612829, "longitude": -73.9768677 }, "span": { "latitudeDelta": 0.008983199999995861, "longitudeDelta": 0.01186000000001286 } }, "coordinate": { "latitude": 40.7617238, "longitude": -73.9777654 }, "formattedAddress": "11 W 53rd St, New York, NY 10019, United States", "countryCode": "US", "telephone": "+12127089400", "urls": [ "http://www.moma.org" ], "country": "United States", "administrativeArea": "New York", "administrativeAreaCode": "NY", "locality": "New York", "postCode": "10019", "subLocality": "Manhattan", "thoroughfare": "W 53rd St", "subThoroughfare": "11", "fullThoroughfare": "11 W 53rd St", "areasOfInterest": [ "Manhattan" ], "dependentLocalities": [ "Midtown Center", "Midtown East", "Midtown Manhattan", "Midtown", "North Hudson" ], "timezone": "America/New_York", "timezoneSecondsFromGmt": -14400 } which gives me "pointOfInterestCategory": "Museum" I think pointOfInterestCategory should also be returned in the placeLookup and might be a mapkit error that its not it would also be cool if search autocomplete gave me the poi so i could tag the search result previews (mapbox does this). Unrelated from this topic but coming from mapbox where everything had a mapbox_id i feel like some things like localities like "columbus, Ohio" should still have a place id but maybe the muid serves that purpose idk and just something i should account for. Lastly on my mind is how to manage rate limiting since im just giving the same mapbox js token to all the clients. Of course for server api I can manage my own system for rate limiting logged in users a bit easier but not sure about mapkit js though I can only dream my project is big enough i need to even worry about that lol.
1
0
682
Jul ’24
MKMapview overlay renderding performance issue on iOS16
Hi , I have following scenario where I feel performance issue. Use-case: I have multiple Overlays(MKOverlay) rendered on MapView, and overlay needs to refresh on point Drag(MKPinAnnotation). I have custom logic to handle drag behaviour of annotation, on annotation drag I do update the overlay. As point update, I create new overlay with updated coordinate and re-render it. iT slow down the performance after few overlay added. Additional Notes: Performance was quite good on iOS16 but on iOS17, it lags the perforce on point drag. When I say it the performance, it point drag lags so it slow the overlay rendering. I am using MKMapView inside SwiftUI. I am sharing code-snippet where it re-render the overlay. Please help with issue in my code implementation. func renderSegments(mapView: MKMapView, segmentPoint: FencePointAnnotation, renderNeeded: Bool = true) { mapViewModel.updateFencePointOrder() guard let activeLayer = mapViewModel.activeLayer else { debugPrint("Invalid active layer.") return } let segments = mapViewModel.activeFence.connectedSegmentsOf(vertex: segmentPoint) // Remove existing overlay. for overlay in mapView.overlays { if let overlay = overlay as? FenceOverlay { if overlay.layerId == activeLayer.layerId { mapView.removeOverlay(overlay) } } else if let overlay = overlay as? FenceSegmentPolyline { if overlay.layerId == activeLayer.layerId { for segment in segments.values where segment.identifier == overlay.identifier { mapView.removeOverlay(overlay) } } } } // When vertex removed the no need to add segment if renderNeeded { if let segments = mapViewModel.updatedSegements(segment: segments.map({$0.key})) { let updatedSegments = mapView.updatedSegmentsWithOffset(segments: segments, layer: activeLayer) mapView.addOverlays(updatedSegments) } } }
5
1
840
Jul ’24