You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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()
    }
}

问题分析

  1. 位置管理逻辑错误:在body中创建CLLocationManager会导致每次视图刷新都重新实例化,无法持续监听位置更新;同步获取locationManager.location时,异步位置请求还未完成,直接走了默认地址。
  2. 初始化时机错误:在init中直接获取CLLocationManager().location,此时还未发起位置请求,必然返回nil。
  3. SunKit参数使用不当:手动计算时区偏移量容易出错,SunKit支持直接传入TimeZone对象,无需手动转换。
  4. 缺少权限状态处理:未处理用户拒绝位置权限的场景,也没有提示用户开启权限。

修改后的代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 07:55:21