如何修改SwiftUI代码使SunKit基于用户位置计算日出日落时间
如何修改SwiftUI代码让SunKit基于用户实际位置计算日出日落时间?
我正在测试SwiftUI代码以实现:在地图上显示用户位置及日出日落时间,选用了SunKit依赖包。当前代码在无法获取位置时默认使用苹果总部地址,但运行后无法基于用户实际位置计算日出日落时间,请问该如何修改?
原代码:
import SwiftUI import SunKit import CoreLocation struct ContentView: View { let timeFormatter: DateFormatter = { let tf = DateFormatter() tf.dateFormat = "HH:mm" return tf }() @State var location: CLLocation? let timeZone = TimeZone.current public init() { self.location = CLLocationManager().location } var body: some View { let sun: Sun let timeZoneOffset = Double(timeZone.secondsFromGMT()) / 3600.0 let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() if let userLocation = locationManager.location { sun = Sun(location: userLocation, timeZone: timeZoneOffset) } else { sun = Sun(location: CLLocation(latitude: 37.7749, longitude: -122.4194), timeZone: 0) } return VStack { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) Text("Hello, world!") Text("Sunrise time: \(timeFormatter.string(from: sun.sunrise))") Text("Azimuth: \(sun.sunriseAzimuth)°") Text("Sunrise: \(sun.sunrise)") Text("Location: \(sun.location)") } .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
问题分析
- 位置管理逻辑错误:在
body中创建CLLocationManager会导致每次视图刷新都重新实例化,无法持续监听位置更新;同步获取locationManager.location时,异步位置请求还未完成,直接走了默认地址。 - 初始化时机错误:在
init中直接获取CLLocationManager().location,此时还未发起位置请求,必然返回nil。 - SunKit参数使用不当:手动计算时区偏移量容易出错,SunKit支持直接传入
TimeZone对象,无需手动转换。 - 缺少权限状态处理:未处理用户拒绝位置权限的场景,也没有提示用户开启权限。
修改后的代码
import SwiftUI import SunKit import CoreLocation // 封装位置管理逻辑为ObservableObject,用于监听位置变化和权限状态 class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { private let manager = CLLocationManager() @Published var userLocation: CLLocation? @Published var authorizationStatus: CLAuthorizationStatus = .notDetermined override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers // 根据需求调整精度 manager.requestWhenInUseAuthorization() manager.startUpdatingLocation() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 取最新的位置 if let latestLocation = locations.last { userLocation = latestLocation // 可以选择获取一次后停止更新,或者持续更新 manager.stopUpdatingLocation() } } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus // 如果用户授权,开始更新位置 if authorizationStatus == .authorizedWhenInUse || authorizationStatus == .authorizedAlways { manager.startUpdatingLocation() } } } struct ContentView: View { let timeFormatter: DateFormatter = { let tf = DateFormatter() tf.dateFormat = "HH:mm" tf.timeZone = .current return tf }() @StateObject private var locationManager = LocationManager() private let defaultLocation = CLLocation(latitude: 37.7749, longitude: -122.4194) var body: some View { VStack(spacing: 16) { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) // 处理权限状态提示 switch locationManager.authorizationStatus { case .notDetermined: Text("正在请求位置权限...") case .denied, .restricted: Text("请在设置中开启位置权限") case .authorizedWhenInUse, .authorizedAlways: if let userLocation = locationManager.userLocation { // 使用用户实际位置和当前时区创建Sun对象 let sun = Sun(location: userLocation, timeZone: .current) Text("日出时间: \(timeFormatter.string(from: sun.sunrise))") Text("日出方位角: \(String(format: "%.1f", sun.sunriseAzimuth))°") Text("日落时间: \(timeFormatter.string(from: sun.sunset))") Text("当前位置: \(userLocation.coordinate.latitude), \(userLocation.coordinate.longitude)") } else { Text("正在获取位置...") } @unknown default: Text("未知权限状态") } } .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
关键修改点
- 分离位置管理逻辑:将
CLLocationManager封装为ObservableObject,通过@Published属性向视图传递位置和权限状态,避免在body中重复创建管理器。 - 异步监听位置更新:通过
CLLocationManagerDelegate的didUpdateLocations方法获取异步返回的位置数据,确保拿到有效位置后再计算日出日落时间。 - 正确使用SunKit:直接传入
TimeZone.current给SunKit的初始化方法,无需手动计算时区偏移量,避免错误。 - 添加权限状态处理:根据不同的权限状态显示对应提示,提升用户体验。
- 优化位置精度:设置合适的位置精度(如
kCLLocationAccuracyThreeKilometers),平衡定位速度和精度需求。
内容的提问来源于stack exchange,提问作者Sees Habu
相关产品推荐
相关产品推荐

