How to get Current Location with SwiftUI?

2020-06-12 04:16发布

问题:

Trying to get current location with using swiftUI. Below code, couldn't initialize with didUpdateLocations delegate.

class GetLocation : BindableObject {
    var didChange = PassthroughSubject<GetLocation,Never>()

    var location : CLLocation {
        didSet {
            didChange.send(self)
        }
    }
    init() {}
}

回答1:

This code below works (Not production ready). Implementing the CLLocationManagerDelegate works fine and the lastKnownLocation is updated accordingly.

Don't forget to set the NSLocationWhenInUseUsageDescription in your Info.plist

class LocationManager: NSObject, CLLocationManagerDelegate, BindableObject {
    private let manager: CLLocationManager
    var didChange = PassthroughSubject<LocationManager, Never>()

    var lastKnownLocation: CLLocation? {
        didSet {
            didChange.send(self)
        }
    }

    init(manager: CLLocationManager = CLLocationManager()) {
        self.manager = manager
        super.init()
    }

    func startUpdating() {
        self.manager.delegate = self
        self.manager.requestWhenInUseAuthorization()
        self.manager.startUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print(locations)
        lastKnownLocation = locations.last
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse {
            manager.startUpdatingLocation()
        }
    }
}


回答2:

As of Xcode 11 beta 4, you will need to change didChange to willChange:

var willChange = PassthroughSubject<LocationManager, Never>()

var lastKnownLocation: CLLocation? {
    willSet {
        willChange.send(self)
    }
}


回答3:

I have written a one-file swift package with usage instructions on https://github.com/himbeles/LocationProvider. It provides a ObservableObject-type wrapper class for CLLocationManager and its delegate. There is a published property location which can directly be used in SwiftUI, as well as a PassthroughSubject that you can subscribe to via Combine. Both update on every didUpdateLocations event of the CLLocationManager.

It also handles the case where location access has previously been denied: The default behavior is to present the user with a request to enable access in the app settings and a link to go there.

In SwiftUI, use as

import SwiftUI
import LocationProvider

struct ContentView: View {
    @ObservedObject var locationProvider : LocationProvider

    init() {
        locationProvider = LocationProvider()
        do {try locationProvider.start()} 
        catch {
            print("No location access.")
            locationProvider.requestAuthorization()
        }
    }

    var body: some View {
        VStack{
        Text("latitude \(locationProvider.location?.coordinate.latitude ?? 0)")
        Text("longitude \(locationProvider.location?.coordinate.longitude ?? 0)")
        }
    }
}