Anyone able to see how to use the new SwiftUI Map and WWDC @Observable concept to dynamically update my SwiftUI Map position and rotation based on the dynamic changes it picks up from my @Observable object.
Note the updates are coming through as the Text labels show this. But how to get the Map position referencing the same values and updating them? The "onAppear" approach doesn't seem to work.
import SwiftUI
import MapKit
@Observable
final class NewLocationManager : NSObject, CLLocationManagerDelegate {
var location: CLLocation? = nil
var direction: CLLocationDirection = 0
private let locationManager = CLLocationManager()
func startCurrentLocationUpdates() async throws {
if locationManager.authorizationStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
print("NewLocationManager: \(location.coordinate.latitude), \(location.coordinate.longitude)")
self.location = location
self.direction = self.direction + 1
}
}
}
struct ContentView: View {
var locationMgr = NewLocationManager()
@State private var mapCamPos: MapCameraPosition = .automatic
private let bigBen = CLLocationCoordinate2D(latitude: 51.500685, longitude: -0.124570)
var body: some View {
ZStack {
Map(position: $mapCamPos)
.onAppear { // Does NOT work - how to get position/direction updates working to Map (map should be moving/rotating)
mapCamPos = .camera(MapCamera(
centerCoordinate: self.locationMgr.location?.coordinate ?? bigBen,
distance: 800,
heading: self.locationMgr.direction
))
}
VStack (alignment: .leading) {
Text("Location from observable: \(locationMgr.location?.description ?? "NIL")") // This works (they get updates regularly)
Text("Direction from observable: \(locationMgr.direction)") // This works (they get updates regularly)
Spacer()
}
}
.task {
try? await locationMgr.startCurrentLocationUpdates()
}
}
}
#Preview {
ContentView()
}
Tag: wwdc2023-10043
MapKit
RSS for tagDisplay 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
143 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Does the API: https://maps-api.apple.com/v1/directions currently not support public transportation queries, such as subway routes?
Is it free to use MapKit's MKLocalSearchCompleter and MKLocalSearch on iOS currently?
I would like to use them for getting geocode.
I found this forum.
https://developer.apple.com/forums/thread/127493
According to this forum, using MapKit for native app development, there is no cost beyond my Apple Developer Program membership.
Is it the same now?
sincerely
Trying to use new Swift @Observable to monitor GPS position within SwiftUI content view. But how do I tie the latest locations to the SwiftUI Map's mapCameraPosition?
Well ideally the answer could cover:
How to fix this error - So get map tracking along with the User Position, but also
How to include facility to turn on/off the map moving to track the user position (which I'll need to do next). So could be tracking, then disable, move map around and have a look at things, then click button to start syncing the mapcameraposition to the GPS location again
Refer to error I'm embedded in the code below.
import SwiftUI
import MapKit
@Observable
final class NewLocationManager : NSObject, CLLocationManagerDelegate {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func startCurrentLocationUpdates() async throws {
if locationManager.authorizationStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
self.location = location
}
}
}
struct ContentView: View {
var newlocationManager = NewLocationManager()
@State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion(
center: newlocationManager.location?.coordinate ?? <#default value#>,
span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25)
))
// GET ERROR: Cannot use instance member 'newlocationManager' within property initializer; property initializers run before 'self' is available
var body: some View {
ZStack {
Map(position: $cameraPosition)
Text("New location manager: \(newlocationManager.location?.description ?? "NIL" )") // works
}
.task {
try? await newlocationManager.startCurrentLocationUpdates()
}
}
}
#Preview {
ContentView()
}
In SwitUI when using a map within a tabview the tab bar color is translucent when the map tab is selected. All of the other tabs the tab bar is opaque. In the view that contains the map, if the map is commented out the tab display is opaque, only when the map is displayed.
I have an init in the tabview to set the tab bar parameters:
// Set Tab Bar appearance
let tabbarAppearance = UITabBarAppearance()
tabbarAppearance.configureWithOpaqueBackground()
tabbarAppearance.backgroundColor = .blue
UITabBar.appearance().standardAppearance = tabbarAppearance
UITabBar.appearance().scrollEdgeAppearance = tabbarAppearance
In the view with the map I use a ZStack to set the entire screen color:
ZStack(alignment: Alignment(horizontal: .center, vertical: .top)) {
Color(.blue)
.ignoresSafeArea(.all)
.foregroundColor(.black)
VStack(alignment: .center, spacing: 5) {
Spacer()
Map()
Spacer()
}
}
I've tried using .padding(), frame, removing ignoresSafeArea but the tab bar still becomes translucent when the tab is selected. Selecting another tab the color becomes opaque.
Any suggestions on how to correct this behavior so the tab bar is opaque for all tabs?
Hi. I want to make iOS app that when use camera AR, it can show someplace around me have annotations. ARGeoAnchor something but I don't have any idea. Can anyone give me some keywords? I can use Mapkit to search but don't know how to map it in AR.
I have a code that redirects the user to Google Maps or Apple Maps by pressing the map , when Apple Maps is uninstalled, an alert appears asking the user to download Apple Maps and redirects the user to the App Store, when I select Show in App Store, the user is redirected to App Store, and then the alert is shown again.
func openMaps() {
let coordinate = CLLocationCoordinate2D(latitude: 52.5162746, longitude: 13.3755153)
let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: coordinate))
mapItem.name = "Test"
mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDefault])
}
Hardware: Apple TV 4K (1st gen)
OS: tvOS 17.1
I have been attempting to create a simple dashboard using MapKit for my company's devices located around the globe. As of now, we only have 7-8 attached to the main database, so I'm using an API client I created to load that many Map markers after the View is loaded.
My issue is that despite this being a relatively low-intensity task, the Map view crashes after about 15 minutes, with no errors or any indications of memory leaks or other problems that would indicate something is wrong. GPU usage spikes on initial load, but remains in the green throughout the process. I tried the same code in swift playgrounds and was able to get the globe to spin indefinitely, probably because it's using the extra resources available on my M2 Max chip. But again, I am well below the memory and CPU usage limits available for tvOS. other than initially loading 7-8 objects asynchornously on initial load, no other work is being done.
MRE:
import SwiftUI
import MapKit
struct ContentView: View {
let timer = Timer.publish(every: 0.1, on: .main, in: .default).autoconnect()
@State var viewport: MapCameraPosition = .region(MKCoordinateRegion(center: .init(latitude: 0, longitude: 0), span: .init(latitudeDelta: 90, longitudeDelta: 180)))
@State var longitude: Double = 0 {
didSet {
viewport = .region(MKCoordinateRegion(center: .init(latitude: 20, longitude: longitude), span: .init(latitudeDelta: 90, longitudeDelta: 180)))
}
}
private func shiftLongitude(by: Double) {
if self.longitude >= 180 {
self.longitude -= 360
} else {
self.longitude += by
}
}
var body: some View {
Map(position: $viewport) {
}
.mapStyle(.imagery(elevation: .realistic))
.onReceive(timer) { _ in
withAnimation {
self.shiftLongitude(by: 1.0)
}
}
}
}
Thank you all in advance.
I've checked the terms and the pre-posted forum related to the same question.
https://developer.apple.com/forums/thread/127493
I have plans to use MapSDK in my native app and is critical to know that it really is free to use.
Though I've checked the answers, I was wondering if there's a written document that says it's free to use on native apps.
This question has been asked several times by other users before. But there is no solution provided it seems. So I am asking the same here. I have a screen where I add mapview as a subview. In that it is showing instead of "Legal".
Demonstrated here in the PlugShare app.
I'm experiencing crashes in our Application when displaying an MKMapView. The crash seems to be happening at a pretty low level and it's difficult to diagnose what the issue is.
It's only happening on devices running iOS 17.1 or later, not happening in the simulator.
All we are doing in the code is initializing and MKMapView and adding to a ViewController view, causes the crash. Our App has been around for a while and was working prior to the roll out of iOS 17.1.
Replaced the App name in the crash report for privacy.
My analysis is that in thread 35 you can see GeoServices and VectorKit libraries being called, VectorKit appears to trying to be calling string length on an null string.
Crash Report
crasher.txt
Crashing thread from report
Thread 35 name:
Thread 35 Crashed:
0 libsystem_platform.dylib 0x000000021d94f244 _platform_strlen + 4 (:-1)
1 VectorKit 0x00000001d06aafcc std::__1::__constexpr_strlen[abi:v160006](char const*) + 28
2 VectorKit 0x00000001d06a5f58 std::__1::char_traits::length(char const*) + 28
3 VectorKit 0x00000001d06aae70 std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator>::basic_string[abi:v160006]std::nullptr_t(char const*) + 60
4 VectorKit 0x00000001d0aaa1c8 invocation function for block in md::TiledGEOResourceFetcher::fetchResources(std::__1::unordered_map<gdc::ResourceKey, unsigned long long, gdc::ResourceKeyHash, std::__1::equal_togdc::ResourceKey... + 1364 (MDTiledGEOResourceFetcher.mm:290)
5 GeoServices 0x00000001ba77f9b0 __143-[GEOTileLoader loadKey:additionalInfo:priority:forClient:auditToken:options:reason:qos:signpostID:createTime:callbackQ:beginNetwork:callback:]_block_invoke + 276 (GEOTileLoader.mm:1066)
6 GeoServices 0x00000001ba78b8b4 invocation function for block in -[GEOTileLoader _loadedTileForLocalKey:preliminary:quickly:tileDecoder:data:disburseTile:]::$_7::operator()(LoadItem::Requester const&) const + 96 (GEOTileLoader.mm:2167)
7 libdispatch.dylib 0x00000001bbf6c250 _dispatch_block_async_invoke2 + 148 (queue.c:555)
8 libdispatch.dylib 0x00000001bbf5d300 _dispatch_client_callout + 20 (object.m:561)
9 libdispatch.dylib 0x00000001bbf607b8 _dispatch_continuation_pop + 600 (queue.c:306)
10 libdispatch.dylib 0x00000001bbf5fdd4 _dispatch_async_redirect_invoke + 584 (queue.c:830)
11 libdispatch.dylib 0x00000001bbf6ebe4 _dispatch_root_queue_drain + 392 (queue.c:7051)
12 libdispatch.dylib 0x00000001bbf6f3ec _dispatch_worker_thread2 + 156 (queue.c:7119)
13 libsystem_pthread.dylib 0x000000021d9f9928 _pthread_wqthread + 228 (pthread.c:2642)
14 libsystem_pthread.dylib 0x000000021d9f9a04 start_wqthread + 8 (:-1)
Hi , I'm trying to teach myself the new Mapkit code. Can someone help me fix the code below:
I'm getting the following error on Line 19: Initializer 'init(coordinateRegion:interactionModes:showsUserLocation:userTrackingMode:annotationItems:annotationContent:)' requires that 'Marker' conform to 'MapAnnotationProtocol'
import MapKit
struct IdentifiableAnnotation: Identifiable {
let id = UUID()
var annotation: MKPointAnnotation
}
struct ContentView: View {
@ObservedObject var locationManager: LocationManager
@State private var showUserProfile = false
@State private var showNotifications = false
@State private var showFriendz = false
@State private var courtAnnotations: [IdentifiableAnnotation] = []
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
Map(coordinateRegion: $locationManager.region,
interactionModes: .all,
showsUserLocation: true,
userTrackingMode: .none,
annotationItems: courtAnnotations) { item in
Marker(<#LocalizedStringKey#>, coordinate: item.annotation.coordinate)
}
.edgesIgnoringSafeArea(.all)
I've encountered a weird issue with the new Map for iOS 17. In my list, which includes a MapView among other elements, I've observed that with the new initializer, the top and bottom bars are persistently displayed. They don't hide and only appear when scrolling, as they should. This problem doesn't occur with the old, now-deprecated initializer. To illustrate this, I have two screenshots: one with the map enabled and the other with the map disabled, demonstrating the issue.
Here is also my new Map code:
struct MapListRowView: View {
@Binding internal var location: LocationModel
@State internal var user: Bool = true
private var position: CLLocationCoordinate2D { .init(latitude: location.latitude, longitude: location.longitude) }
private var isPad: Bool { UIDevice.current.userInterfaceIdiom == .pad ? true : false }
var body: some View {
Map(bounds: .init(minimumDistance: 1250)) {
if user { UserAnnotation() }
Annotation("", coordinate: position) {
ZStack {
Circle().fill().foregroundColor(.white).padding(1)
Image(systemName: "mappin.circle.fill")
.resizable()
.foregroundColor(.indigo)
}.frame(width: 20, height: 20).opacity(user ? .zero : 1.0)
}
}
.frame(height: isPad ? 200 : 100)
.cornerRadius(8)
.listRowInsets(.init(top: -5, leading: .zero, bottom: -5, trailing: .zero))
.padding(.vertical, 5)
.disabled(true)
}
}
I want to generate randomised MKLookAroundScene for my world exploration app (think geogussr functionality). How can i achieve that with MapKit? I have tried
struct LookAroundView: View {
@State private var lookAroundScene: MKLookAroundScene?
@State private var yOffset: CGFloat = 0
func generateRandomCoordinate() {
let minLatitude = -90.0
let maxLatitude = 90.0
let minLongitude = -180.0
let maxLongitude = 180.0
let randomCoordinate = CLLocationCoordinate2D(
latitude: Double.random(in: minLatitude...maxLatitude),
longitude: Double.random(in: minLongitude...maxLongitude)
)
checkLookAroundAvailability(coordinate: randomCoordinate)
}
func checkLookAroundAvailability(coordinate: CLLocationCoordinate2D) {
Task {
let request = MKLookAroundSceneRequest(coordinate: coordinate)
if let scene = try? await request.scene {
DispatchQueue.main.async {
self.lookAroundScene = scene
}
} else {
generateRandomCoordinate()
}
}
}
var body: some View {
Group {
if let scene = lookAroundScene {
LookAroundPreview(initialScene: scene)
} else {
Button(action: {
generateRandomCoordinate()
}) {
Text("Generate Random Coordinate")
}
.offset(y: yOffset)
}
}
}
}
but it didn't work. I put the yOffset change there to debug, and it seems that self.lookAroundScene = scene never gets executed.
If there are any other options, please let me know!
Hello all, I am playing with MapKit for SwiftUI, so far so good. There is one thing I have not seen any documentations, or sample codes around and that's elevation data, e.g.
My questions are:
Is there a way to get this information from an MKRoute?
Is it possible to get the elevation gain/drop at a given point in the route?
Many thank in advance for your help.
Try the following code on macOS, and you'll see the marker is added in the wrong place, as the conversion from screen coordinates to map coordinates doesn't work correctly.
The screenCoord value is correct, but reader.convert(screenCoord, from: .local) offsets the resulting coordinate by the height of the content above the map, despite the .local parameter.
struct TestMapView: View {
@State var placeAPin = false
@State var pinLocation :CLLocationCoordinate2D? = nil
@State private var cameraProsition: MapCameraPosition = .camera(
MapCamera(
centerCoordinate: .denver,
distance: 3729,
heading: 92,
pitch: 70
)
)
var body: some View {
VStack {
Text("This is a bug demo.")
Text("If there are other views above the map, the MapProxy doesn't convert the coordinates correctly.")
MapReader { reader in
Map(
position: $cameraProsition,
interactionModes: .all
)
{
if let pl = pinLocation {
Marker("(\(pl.latitude), \(pl.longitude))", coordinate: pl)
}
}
.onTapGesture(perform: { screenCoord in
pinLocation = reader.convert(screenCoord, from: .local)
placeAPin = false
if let pinLocation {
print("tap: screen \(screenCoord), location \(pinLocation)")
}
})
.mapControls{
MapCompass()
MapScaleView()
MapPitchToggle()
}
.mapStyle(.standard(elevation: .automatic))
}
}
}
}
extension CLLocationCoordinate2D {
static var denver = CLLocationCoordinate2D(latitude: 39.742043, longitude: -104.991531)
}
(FB13135770)
In UIKit, we can add an insets to a MKMapView with setVisibleMapRect to have additional space around a specified MKMapRect. It's useful for UIs like Apple Maps or FlightyApp (see first screenshot attached). This means we can have a modal sheet above the map but still can see all the content added to the map.
I'm trying to do the same for a SwiftUI Map (on iOS 17) but I can't find a way to do it: see screenshot 2 below. Is it possible to obtain the same result or should I file a feedback for an improvement?
Hi,
I'm looking through SwiftUI Map for SwiftUI documentation (including IOS17 Beta) for way to adjust Map() scale, or zoom level, while simultaneously showing user's location and heading, for which I'm doing this
@State var position = MapCameraPosition = .userLocation(followsHeading: true, fallback: .automatic)
Map(position: $position)
It does not appear to be possible so am looking for confirmation. Thanks everyone.
According to the MKMapview, all annotations should be added to the map:
https://developer.apple.com/documentation/mapkit/mkmapview
When configuring your map interface, be sure to add all of your annotation objects right away. The map view uses the coordinate data in each annotation object to determine when the corresponding annotation view needs to appear onscreen. When an annotation moves onscreen, the map view asks its delegate to create a corresponding annotation view. If your app has different types of annotations, it can define different annotation view classes to represent each type.
But this has a very low performance when the map is zoomed out and all annotations are visible (although market already filters out most of the annotations and only displays a few). As fas as I've seen, the suggestion from the documentation doesn't scale properly when the number of annotations is very large.
Grouping doesn't help, neither using reusable annotations.
Is the only option here, handling manually the add/remove annotations as the map rect changes?