在 iOS 14 Widget 中获取当前位置

Nik*_*nov 7 core-location ios swift widgetkit

有没有人尝试过在 iOS 14 Widget 中更新用户的位置?在阅读了 Apple Developer 论坛后,我想出了编写包装器CLLocationManager并以这种方式使用它:

class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
    var locationManager: CLLocationManager? {
        didSet {
            self.locationManager!.delegate = self
        }
    }
    private var handler: ((CLLocation) -> Void)?
    
    func fetchLocation(handler: @escaping (CLLocation) -> Void) {
        self.handler = handler
        self.locationManager!.requestLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.handler!(locations.last!)
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error)
    }
}
Run Code Online (Sandbox Code Playgroud)

并以这种方式使用它:

var widgetLocationManager = WidgetLocationManager()
    func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        if widgetLocationManager.locationManager == nil {
            widgetLocationManager.locationManager = CLLocationManager()
            widgetLocationManager.locationManager!.requestWhenInUseAuthorization()
        }
        widgetLocationManager.fetchLocation(handler: { location in
            print(location)
            .......
        })
    }
Run Code Online (Sandbox Code Playgroud)

我在 Widget 中也有这 2 个条目info.plist

<key>NSLocationUsageDescription</key>
<string>1</string>

<key>NSWidgetWantsLocation</key>
<true/>
Run Code Online (Sandbox Code Playgroud)

当 locationManager.requestLocation() 被调用时,授权状态是 authorisedWhenInUse,但委托的方法永远不会被调用。我错过了什么?

sab*_*ius 7

首先,我看到的明显问题:

<key>NSLocationUsageDescription</key>
<string>1</string>
Run Code Online (Sandbox Code Playgroud)

NSLocationUsageDescription已弃用:Apple Documentation,因此您应该使用NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescription代替。请务必在主应用程序Info.plist中也包含您选择的权限

此外,CLLocationManager在创建

func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

可能有问题,因为它可以从后台线程调用,所以我会WidgetLocationManager像这样重构你:

class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
    var locationManager: CLLocationManager? 
    private var handler: ((CLLocation) -> Void)?

    override init() {
        super.init()
        DispatchQueue.main.async {
            self.locationManager = CLLocationManager()
            self.locationManager!.delegate = self
            if self.locationManager!.authorizationStatus == .notDetermined {
                self.locationManager!.requestWhenInUseAuthorization()
            }
        }
    }
    
    func fetchLocation(handler: @escaping (CLLocation) -> Void) {
        self.handler = handler
        self.locationManager!.requestLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.handler!(locations.last!)
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

var widgetLocationManager = WidgetLocationManager()

func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
    widgetLocationManager.fetchLocation(handler: { location in
        print(location)
        .......
    })
}
Run Code Online (Sandbox Code Playgroud)