SwiftUI Map使用MapCamera时GPS更新卡顿而非平滑动画问题
SwiftUI Map 相机平滑跟随用户导航的解决方案
你的卡顿问题根源在于手动监听GPS位置更新并直接设置相机位置,即使添加了withAnimation,也无法匹配GPS离散更新的节奏,导致动画衔接断裂。而Apple Maps/Google Maps的平滑效果依赖系统内置的位置插值与相机过渡逻辑,而非手动触发动画。
核心解决方案
放弃手动设置MapCamera,改用SwiftUI Map内置的.userLocation跟踪模式,同时通过mapCameraConfiguration自定义相机参数(距离、俯仰角、航向),让系统自动处理平滑过渡。
修改后的完整代码
import SwiftUI import MapKit import CoreLocation struct ContentView: View { @State private var locationManager = LocationManagerDelegate() @State private var cameraPosition: MapCameraPosition = .userLocation(followsHeading: true, fallback: .automatic) @State private var isTracking: Bool = false var body: some View { Map(position: $cameraPosition) { UserAnnotation() } .mapCameraConfiguration { config in // 自定义相机参数,系统会自动平滑应用这些值 config.distance = 1000 config.pitch = 60 if let course = locationManager.location?.course, course >= 0 { config.heading = course } } .safeAreaInset(edge: .bottom) { Button(isTracking ? "Stop Tracking" : "Track") { isTracking.toggle() if isTracking { locationManager.requestPermission() locationManager.startNavigating() } else { locationManager.stopNavigating() } } .buttonStyle(.glassProminent) .buttonSizing(.flexible) .controlSize(.extraLarge) .padding(.horizontal) } } } @MainActor @Observable final class LocationManagerDelegate: NSObject, CLLocationManagerDelegate { var location: CLLocation? var authorizationStatus: CLAuthorizationStatus = .notDetermined let manager = CLLocationManager() private var liveUpdateTask: Task<Void, Never>? override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation // 开启航向更新,确保course数据可用 manager.headingFilter = kCLHeadingFilterNone manager.startUpdatingHeading() authorizationStatus = manager.authorizationStatus } func requestPermission() { manager.requestWhenInUseAuthorization() } func startNavigating() { liveUpdateTask = Task { do { for try await update in CLLocationUpdate.liveUpdates(.automotiveNavigation) { guard let newLocation = update.location else { continue } self.location = newLocation } } catch { print("Live updates error: \(error)") } } } func stopNavigating() { liveUpdateTask?.cancel() liveUpdateTask = nil manager.stopUpdatingLocation() manager.stopUpdatingHeading() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { location = locations.last } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus } }
关键修改说明
- 相机跟踪模式切换:使用
.userLocation(followsHeading: true)让系统自动处理位置与航向的平滑跟踪,这是实现类似Apple Maps流畅效果的核心。 - 自定义相机参数:通过
mapCameraConfiguration设置固定的距离(1000)和俯仰角(60),并动态绑定航向到用户的course,系统会自动插值这些参数的变化,避免卡顿。 - 完善LocationManager:添加
startUpdatingHeading()并设置headingFilter,确保航向数据(course)能稳定获取,让相机航向跟随更流畅。
额外注意事项
- 确保Info.plist中添加
Privacy - Location When In Use Usage Description权限描述,部分设备可能还需要Privacy - Motion Usage Description以获取航向数据。 - 模拟器中使用
Freeway Drive测试时,确保航向数据正常输出,系统会自动平滑过渡相机位置与航向。
内容的提问来源于stack exchange,提问作者Ozy
相关产品推荐
相关产品推荐

