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

146 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

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
310
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
464
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
271
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
342
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
323
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
0
424
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
494
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
545
Jul ’24
SwiftUI Map Annotation Coordinate Animation
Hi, I have a SwiftUI Map with a set of three annotations. These annotations move around, and I would like to animate their movement from one coordinate to another, but I'm not finding a way to do that. I've tried using withAnimation { } when setting my array of Identifiable models that back the Annotations, and I've tried adding the .animation(.default, annotationModels) modifier to my Map object (where annotationModels is the array that backs my Annotations). The animation modifier doesn't work on Annotation structs, and it doesn't work if I add the animation modifier within the Annotation's view either. Does anyone have any suggestions on how I might be able to animate the coordinates of annotations using a SwiftUI Map? Does the problem have to do with the fact that I have an array of these annotations?
1
1
425
Jul ’24
SwiftUI navigate to another view from a popup
when I click on a mapview pin, a PopupView comes up with some text and a button. I want to be able to navigate to another DetailsView by clicking on the button display in the popup, the button is embedded inside NavigationLink. But clicking on the button nothing happens. How to navigate from button click? struct MyMapView: View { @State var position: MapCameraPosition = .automatic @State var showCallout: Bool = false @State var selected: PinAnnotation? @Binding var locationPins: [PinAnnotation] @State private var toggler = false var body: some View { Map(position: $position) { ForEach(locationPins, id: \.self) { result in Annotation(result.title!, coordinate: result.coordinate) { ZStack { Image(systemName: "mappin.circle.fill") .resizable() .scaledToFit() .frame(width: 30, height: 30) .onTapGesture { selected = result toggler.toggle() } .foregroundStyle(.white, .purple) if selected == result && toggler { PopupView(pin: selected) } else { EmptyView() } } } } } } struct PopupView: View { @State var pin: PinAnnotation? @State private var select: Int? = 0 var body: some View { VStack (alignment: .leading) { HStack { if let val = pin { Text(val.text) .font(.system(size: 12, weight: .light, design: .default)) NavigationLink(destination: DetailsView(), label: { Button(action: {select = 1}){ Image(systemName: "play.circle") } .scaledToFit() .frame(width: 50, height: 50) .background(Color.blue) .foregroundColor(.white) .clipShape(Circle()) }) } else { Text("no data") } } // .fixedSize() } .scaledToFit() .foregroundStyle(.purple) .frame(maxWidth: .infinity) .background(Color.white) .cornerRadius(10) .shadow(radius: 5) .offset(x: 0, y: -45) } } struct DetailsView: View { @Environment(\.presentationMode) var presentation var body: some View { Group { Button("Back") { self.presentation.wrappedValue.dismiss() } } } } }
3
0
394
Jul ’24
Map behaves differently compared to MKMapView
Hey, I have a problem. I was using MKMapView in my app, and in the view where I had a background at the top of the screen, in the example it was Color.red, it extended all the way to the top of the screen. Now, I wanted to switch to the newer Map and I'm seeing an issue because I'm getting a navigation bar that cuts off my color as I indicated in the picture. Does anyone know why this is happening and if there's another way to achieve this? Steps to reproduce: Change MapView() to Map() to see difference import SwiftUI import MapKit @main struct TestAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { NavigationStack { ScrollView(.vertical) { Color.red .padding(.top, -200) .frame(height: 200) MapView().frame(minHeight: 300) // change this line to Map } .navigationTitle("Title") .navigationBarTitleDisplayMode(.large) } } } private typealias ViewControllerRepresentable = UIViewControllerRepresentable struct MapView: ViewControllerRepresentable { typealias ViewController = UIViewController class Controller: ViewController { var mapView: MKMapView { guard let tempView = view as? MKMapView else { fatalError("View could not be cast as MapView.") } return tempView } override func loadView() { let mapView = MKMapView() view = mapView } } func makeUIViewController(context: Context) -> Controller { Controller() } func updateUIViewController(_ controller: Controller, context: Context) { update(controller: controller) } func update(controller: Controller) { } } #Preview { ContentView() } I got: I want:
1
0
386
Jul ’24
Map behaves differently compared to MKMapView
Hey, I have a problem. I was using MKMapView in my app, and in the view where I had a background at the top of the screen, in the example it was Color.red, it extended all the way to the top of the screen. Now, I wanted to switch to the newer Map and I'm seeing an issue because I'm getting a navigation bar that cuts off my color as I indicated in the picture. Does anyone know why this is happening and if there's another way to achieve this? Steps to reproduce: Change MapView() to Map() to see difference import SwiftUI import MapKit @main struct TestAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { NavigationStack { ScrollView(.vertical) { Color.red .padding(.top, -200) .frame(height: 200) MapView().frame(minHeight: 300) // change this line to Map } .navigationTitle("Title") .navigationBarTitleDisplayMode(.large) } } } private typealias ViewControllerRepresentable = UIViewControllerRepresentable struct MapView: ViewControllerRepresentable { typealias ViewController = UIViewController class Controller: ViewController { var mapView: MKMapView { guard let tempView = view as? MKMapView else { fatalError("View could not be cast as MapView.") } return tempView } override func loadView() { let mapView = MKMapView() view = mapView } } func makeUIViewController(context: Context) -> Controller { Controller() } func updateUIViewController(_ controller: Controller, context: Context) { update(controller: controller) } func update(controller: Controller) { } } #Preview { ContentView() } I got: I want:
1
0
424
Jul ’24
Draw Indoor Navigation path using ARKit iOS
I have indoor map and I want to draw path between two route location ex. from A to B I want to draw ARKit based Arrow path in ios Application. Currently I am using ARAnchor to achieve this but challenges is if A to B is 10 meter and I am adding Nodes on each one meter so instead of 10 different nodes i am getting single Arrow nodes showing all 10 in it. I am using below code. // Below Code from where I am calling addArpath function if let lat1 = mCurrentPosition?.latitude, let long1 = mCurrentPosition?.longitude { let latEnd = steplocation.latitude let longEnd = steplocation.longitude // if let lastLat = arrpath.last?.latitude,let lastLong = arrpath.last?.longitude,let lastAltitude = arrpath.last?.altitude{ let userLocation = CLLocation(latitude: lat1, longitude: long1) let endLocation = CLLocation(coordinate: CLLocationCoordinate2DMake(CLLocationDegrees(latEnd), CLLocationDegrees(longEnd)), altitude: CLLocationDistance(steplocation.altitude), horizontalAccuracy: CLLocationAccuracy(5), verticalAccuracy: CLLocationAccuracy(0), course: CLLocationDirection(-1), speed: CLLocationSpeed(5), timestamp: Date()) let heading = getHeadingForDirectionFromCoordinate(from: userLocation, to: endLocation) let lon1 = degreesToRadians(long1) //DegreesToRadians(long1) let lon2 = degreesToRadians(longEnd); //DegreesToRadians(longEnd); let lat2 = degreesToRadians(latEnd); let dLon = lon2 - lon1 let y = sin(dLon) * cos(lat2); yVal = yVal + y // let distanceToendpoint = calculateDistance(lat: endLocation.coordinate.latitude, long: endLocation.coordinate.longitude) let distvalue = Int(distance) + Int(pathlength) distance += CGFloat(distvalue) for i in stride(from: 0, to: distance, by:1) { print("current loop iteration is:" ,i) let headingValue = heading - self.heading zValue = zValue + headingValue distanceVal = CGFloat(i) + distanceVal zGlobal = zValue // Calling addARPathtoLocation addARPathtoLocation(stepLocation:endLocation,zvalue: zValue, yvalue: yVal, distance:Float(i), direction: manuoverType) } // } } // MARK: - ARSessionDelegate func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard !(anchor is ARPlaneAnchor) else { return } let sphereNode = generateArrowNodes(anchor: anchor) DispatchQueue.main.async { node.addChildNode(sphereNode) } } //create ARAnchor to add to nodes func generateArrowNodes(anchor: ARAnchor) -> SCNNode { let imageMaterial = SCNMaterial() imageMaterial.isDoubleSided = true imageMaterial.diffuse.contents = UIImage(named: "blueArrow") let plane = SCNPlane(width:0.5, height:0.5) plane.materials = [imageMaterial] plane.firstMaterial?.isDoubleSided = true let blueNode = SCNNode(geometry: plane) blueNode.name = "blueNode" blueNode.position = SCNVector3(x:Float(zGlobal), y:0, z:Float(distanceVal)) blueNode.eulerAngles.x = -.pi / 2 blueNode.eulerAngles.y -= Float(CGFloat(CGFloat.pi/4*6)) return blueNode } func addARPathtoLocation(stepLocation: CLLocation, zvalue: CGFloat, yvalue: CGFloat, distance:Float, direction:VMEManeuverType) { // give you the depth of anything ARKit has detected guard let query = sceneView.raycastQuery(from: sceneView.center , allowing: .estimatedPlane, alignment: .any) else { return } let results = sceneView.session.raycast(query) guard let hitResult = results.first else { print("No surface found") return } // Add ARAnchor to Scene let anchor = ARAnchor(transform: hitResult.worldTransform) sceneView.session.add(anchor: anchor) } func radiansToDegrees(_ radians: Double) -> Double { return (radians) * (180.0 / Double.pi) } func degreesToRadians(_ degrees: Double) -> Double { return (degrees) * (Double.pi / 180.0) } func getHeadingForDirectionFromCoordinate(from: CLLocation, to: CLLocation) -> Double { let fLat = degreesToRadians(from.coordinate.latitude) let fLng = degreesToRadians(from.coordinate.longitude) let tLat = degreesToRadians(to.coordinate.latitude) let tLng = degreesToRadians(to.coordinate.longitude) let deltaL = tLng - fLng let x = sin(deltaL) * cos(tLng) //cos(tLat) * sin(deltaL) let y = cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(deltaL) let bearing = atan2(x,y) let bearingInDegrees = bearing.toDegrees print("Bearing Degrees :",bearingInDegrees) // sanity check // let degree = radiansToDegrees(atan2(sin(tLng-fLng)*cos(tLat), cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(tLng-fLng))) if bearingInDegrees >= 0 { return bearingInDegrees } else { return bearingInDegrees + 360 } }
0
0
453
Jun ’24
MapKit mapItemDetailAccessory Custom View
Hi all, It wasn't extensively covered in the "Unlock the power of places with MapKit" at WWDC, but is it possible to add your own views to the mapItemDetailAccessory? The default view is great, but I would like to add a button for opening a new window showing another view. The documentation is rather limited at the moment so I thought I would ask here. Thanks in advance.
0
0
356
Jun ’24
mapItemDetailSheet Not Displaying Information
I'm on MacOS 15 Beta and Xcode 16 Beta. Running iOS 18 Beta on a 15 Pro Max. I'm leveraging the .mapItemDetailSheet(item: input) option to pull up a sheet that displays the Place Card for a selection made from a List of places. This is working great on my iPhone 15 Pro Max and my iPad Pro 13. I also have the Mac Catalyst option added under: Project \ General \ Supported Destinations If I launch the app from Xcode via the play button, the app functions fine on my MacBook Pro and I get logs in Xcode, etc. However, if stop the session in Xcode and attempt to run the same app on its own on my MacBook Pro, the mapItemDetailSheet does not populate with any information. Anyone have ideas on where the disconnect could be? Why would the sheet populate with data when ran from Xcode but not when ran on its own? Again, it runs fine on the iPhone and iPad. Any ideas would be helpful and appreciated.
1
0
416
Jul ’24
Apple Maps Pins in Custom Map
I have been developing an app that includes some pinned locations. These are displayed on a map, and currently have the standard pins and SF Symbol pins. A few of the locations have a special symbol on Apple Maps, but the same symbol is not avalable in SF Symbols. I was wondering if it is possible to use the pins, other than the standard red, from Apple Maps.
0
0
474
Jun ’24