From apple documentation Meet MapKit for SwiftUI video I have tried to follow the video tutorial and use UserAnnotation()
function in order to get the user location in simulator as like the video. But it's not working.
BeantownButtons.swift
import SwiftUI
import MapKit
@available(iOS 17.0, *)
struct BeantownButtons: View {
@Binding var searchResults: [MKMapItem]
@Binding var position: MapCameraPosition
var visibleRegion: MKCoordinateRegion?
func search(for query: String) {
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = query
request.resultTypes = .pointOfInterest
request.region = visibleRegion ?? MKCoordinateRegion(
center: .parking,
span: MKCoordinateSpan (latitudeDelta: 0.0125, longitudeDelta: 0.0125))
Task {
let search = MKLocalSearch(request: request)
let response = try? await search.start()
searchResults = response?.mapItems ?? []
print("Search Results: \(searchResults)")
}
}
var body: some View {
HStack {
Button {
search(for: "playground")
} label: {
Label("Playgrounds", systemImage: "figure.and.child.holdinghands")
}
.buttonStyle(.borderedProminent)
Button {
search(for: "beach")
} label: {
Label("Beaches", systemImage: "beach.umbrella")
}
.buttonStyle(.borderedProminent)
Button {
position = .region(.boston)
} label: {
Label("Boston", systemImage: "building.2")
}
.buttonStyle(.bordered)
Button {
position = .region(.northShore)
} label: {
Label("North Shore", systemImage: "water.waves")
}
.buttonStyle(.bordered)
}
.labelStyle(.iconOnly)
}
}
#Preview {
if #available(iOS 17.0, *) {
BeantownButtons(searchResults: .constant([]), position: .constant(.automatic))
} else {
EmptyView()
}
}
ItemInfoView.swift
import SwiftUI
import _MapKit_SwiftUI
@available(iOS 17.0, *)
struct ItemInfoView: View {
// ItemInfoView.swift - Fetch a Look Around scene
@State var lookAroundScene: MKLookAroundScene?
@State var route: MKRoute?
@State var selectedResult: MKMapItem
func getLookAroundScene () {
lookAroundScene = nil
Task {
let request = MKLookAroundSceneRequest(mapItem: selectedResult)
lookAroundScene = try? await request.scene
}
}
// ItemInfoView.swift - Format travel time for display
var travelTime: String? {
guard let route else { return nil }
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated
formatter.allowedUnits = [.hour, .minute]
return formatter.string(from: route.expectedTravelTime)
}
var body: some View {
LookAroundPreview(initialScene: lookAroundScene)
.overlay(alignment: .bottomTrailing) {
HStack {
Text ("\(selectedResult.name ?? "")")
if let travelTime {
Text(travelTime)
}
}
.font(.caption)
.foregroundStyle(.white)
.padding (10)
}
.onAppear {
getLookAroundScene()
}
.onChange(of: selectedResult) {
getLookAroundScene()
}
}
}
#Preview {
if #available(iOS 17.0, *) {
ItemInfoView(selectedResult: MKMapItem(placemark: MKPlacemark(coordinate: .daraz)))
} else {
EmptyView()
}
}
CustomMapViewLookAround.swift
import SwiftUI
import _MapKit_SwiftUI
@available(iOS 17.0, *)
struct CustomMapViewLookAround: View {
@State private var position: MapCameraPosition = .automatic
@State private var visibleRegion: MKCoordinateRegion?
@State var route: MKRoute?
@State var searchResults: [MKMapItem] = []
@State private var selectedResult: MKMapItem?
func getDirections() {
print("results count: \(searchResults.count)")
route = nil
guard let selectedResult else { return }
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: .parking))
request.destination = selectedResult
Task {
let directions = MKDirections(request: request)
let response = try? await directions.calculate()
route = response?.routes.first
}
}
var body: some View {
Map(position: $position, selection: $selectedResult) {
Annotation("Parking", coordinate: .parking) {
ZStack {
RoundedRectangle(cornerRadius: 5)
.fill(.background)
RoundedRectangle(cornerRadius: 5)
.stroke(.secondary, lineWidth: 5)
Image(systemName: "car")
.padding(5)
}
}
.annotationTitles(.hidden)
ForEach(searchResults, id: \.self) { result in
Marker(item: result)
// .tag(1)
}
.annotationTitles(.hidden)
if let route {
MapPolyline(route)
.stroke(.blue, lineWidth: 5)
}
UserAnnotation()
}
.mapStyle(.standard)
.safeAreaInset(edge: .bottom) {
HStack {
Spacer()
VStack(spacing: 0) {
if let selectedResult {
ItemInfoView(route: route, selectedResult: selectedResult)
.frame(height: 128)
.clipShape(RoundedRectangle (cornerRadius: 10))
.padding([.top, .horizontal])
}
BeantownButtons(searchResults: $searchResults, position: $position, visibleRegion: visibleRegion)
.padding(.top)
}
Spacer()
}
.background(.thinMaterial)
}
.onChange(of: searchResults) {
position = .automatic
}
.onMapCameraChange { context in
visibleRegion = context.region
}
.onChange(of: selectedResult) {
getDirections()
}
.mapControls {
MapUserLocationButton()
MapCompass()
MapScaleView()
}
.onAppear {
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}
}
}
#Preview {
if #available(iOS 17.0, *) {
CustomMapViewLookAround()
} else {
EmptyView()
}
}
Error:
CLLocationManager(<CLLocationManager: 0x600000010b20>) for <MKCoreLocationProvider: 0x600003000c60> did fail with error: Error Domain=kCLErrorDomain Code=1 "(null)"
Whenever I click the location button . It buffers forever. I have also add the location access privacy in info.plist. Moreover, set the simulator location like this
Privacy - Location Always Usage Description
Privacy - Location Always and When In Use Usage Description
I've found you still need to use the appropriate privacy keys in your Info.plist, and request location authorisation from CLLocationManager
before the UserAnnotation()
will display.