如何实现带完成回调的首次用户位置更新触发函数?
没问题,我来帮你实现这个带完成回调的位置获取函数,完全适配你在AppDelegate里的使用场景!
实现方案
我们需要给你的LocationManager单例添加一个可选的回调属性,在首次获取到有效位置时触发它,同时避免重复调用。下面是完整的实现步骤:
1. 完善LocationManager单例
首先更新你的LocationManager类,添加回调属性并实现CLLocationManagerDelegate的核心方法:
import CoreLocation class LocationManager: NSObject, CLLocationManagerDelegate { // 单例实例 static let shared = LocationManager() // 位置管理器实例 private let manager = CLLocationManager() // 存储首次获取位置的回调 private var locationCompletion: ((CLLocation?) -> Void)? private override init() { super.init() manager.delegate = self // 设置精度(根据需求调整,这里用最佳精度) manager.desiredAccuracy = kCLLocationAccuracyBest } // 对外暴露的获取位置方法 func getLocation(completion: @escaping (CLLocation?) -> Void) { // 保存回调 locationCompletion = completion // 检查位置权限状态 switch manager.authorizationStatus { case .authorizedWhenInUse, .authorizedAlways: // 已授权,开始更新位置 manager.startUpdatingLocation() case .notDetermined: // 未请求权限,先请求 manager.requestWhenInUseAuthorization() case .denied, .restricted: // 权限被拒绝或受限,直接回调nil completion(nil) locationCompletion = nil @unknown default: completion(nil) locationCompletion = nil } } // 实现反向地理编码获取国家的方法(你提到的fetchCountry) func fetchCountry(from location: CLLocation?, completion: @escaping (String?) -> Void) { guard let location = location else { completion(nil) return } let geocoder = CLGeocoder() geocoder.reverseGeocodeLocation(location) { placemarks, error in if let error = error { print("反向地理编码失败:\(error.localizedDescription)") completion(nil) return } // 从第一个地标中提取国家名称 let country = placemarks?.first?.country completion(country) } } // MARK: - CLLocationManagerDelegate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 取数组中的最后一个位置(通常是最新最准确的) guard let latestLocation = locations.last, locationCompletion != nil else { return } // 停止更新位置,节省资源 manager.stopUpdatingLocation() // 触发回调并清空,避免重复调用 locationCompletion?(latestLocation) locationCompletion = nil } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { // 当权限状态变化时,如果之前有未完成的回调,继续处理 guard let completion = locationCompletion else { return } switch status { case .authorizedWhenInUse, .authorizedAlways: manager.startUpdatingLocation() case .denied, .restricted: completion(nil) locationCompletion = nil default: break } } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("获取位置失败:\(error.localizedDescription)") // 失败时触发回调并清空 locationCompletion?(nil) locationCompletion = nil manager.stopUpdatingLocation() } }
2. 关键逻辑说明
- 回调存储与触发:我们用
locationCompletion这个可选属性保存外部传入的回调,首次获取到有效位置后立即调用它,然后置空,确保只会触发一次。 - 权限处理:在
getLocation方法中先检查权限状态,未授权时会请求权限;权限变化时通过didChangeAuthorization继续处理逻辑,避免用户授权后无法获取位置。 - 资源优化:拿到位置后立即调用
stopUpdatingLocation,避免持续消耗电量。
3. 在AppDelegate中的使用
你之前的代码可以直接适配这个实现,只需要注意把getUserLocation改成我们定义的getLocation即可:
// AppDelegate.swift import UIKit import UserNotifications class AppDelegate: UIResponder, UIApplicationDelegate { let notificationManager = NotificationManager() // 假设你有这个通知管理类 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in if granted { // 调用我们实现的getLocation方法 LocationManager.shared.getLocation(completion: { location in // 调用反向地理编码获取国家 LocationManager.shared.fetchCountry(from: location, completion: { country in if country == "United States" { let notification = LocalNotification() self.notificationManager.schedule(notification: notification) } }) }) } } return true } }
额外注意事项
- 别忘了在
Info.plist中添加位置权限描述,比如NSLocationWhenInUseUsageDescription,否则权限请求会被系统拒绝。 - 如果需要后台获取位置,还需要配置
NSLocationAlwaysAndWhenInUseUsageDescription和开启后台模式中的位置更新权限,但根据你的需求(App启动时获取一次),WhenInUse权限足够。
内容的提问来源于stack exchange,提问作者Swifty
相关产品推荐
相关产品推荐

