如何在没有ViewControllers的情况下使用SwiftUI获取当前位置? [英] How to get Current Location using SwiftUI, without ViewControllers?

查看:85
本文介绍了如何在没有ViewControllers的情况下使用SwiftUI获取当前位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在项目中准备了以下类来检索用户当前位置:

I've prepared in my project the following class to retrieve the user current location:

LocationManager.swift

import Foundation
import CoreLocation


class LocationManager: NSObject {

    // - Private
    private let locationManager = CLLocationManager()

    // - API
    public var exposedLocation: CLLocation? {
        return self.locationManager.location
    }

    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy =     kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
    }
}

// MARK: - Core Location Delegate 
extension LocationManager: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager,
                         didChangeAuthorization status:      CLAuthorizationStatus) {

        switch status {

            case .notDetermined         : print("notDetermined")          // location permission not asked for yet
            case .authorizedWhenInUse   : print("authorizedWhenInUse")  // location authorized
            case .authorizedAlways      : print("authorizedAlways")     // location authorized
            case .restricted            : print("restricted")           // TODO: handle
            case .denied                : print("denied")               // TODO: handle
            default                     : print("unknown")              // TODO: handle
        }
    }
}

// MARK: - Get Placemark
extension LocationManager {

    func getPlace(for location: CLLocation,
              completion: @escaping (CLPlacemark?) -> Void) {

        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location) { placemarks, error in

            guard error == nil else {
                print("*** Error in \(#function): \  (error!.localizedDescription)")
                completion(nil)
                return
            }

            guard let placemark = placemarks?[0] else {
                print("*** Error in \(#function): placemark is nil")
                completion(nil)
                return
            }

            completion(placemark)
        }
    }
}

但是我不确定在使用SwiftUI时如何从ContentView文件中使用它。
我应该如何在不使用标准ViewController中会使用的方法的情况下获取暴露的位置(在这种情况下,使用Guard,Let和Return当然会产生各种错误,因为我不应该这样做如果我正确的话,可以在这种情况下使用return)。
关于如何实现此目标的任何提示?
每当按下按钮时,我都希望获得用户位置(目前我仅使用了模型数据)。

But I'm not sure how to use it, while using SwiftUI, from my ContentView file. How am I supposed to get the exposedLocation without using the approach I would have used in a standard ViewController (in this case the use of guard, let and return of course generates all kind of errors, since I'm not supposed to use returns in this context, if I got it right). Any hint about how to achieve this? I would like to get the user location whenever a button is pressed (at the moment I've used just mockup data).

ContentView.swift
import SwiftUI

struct Location: Identifiable {
    // When conforming to the protocol Identifiable we have to to   implement a variable called id however this variable does not have to be an Int. The protocol only requires that the type of the variable id is actually Hashable.
    // Note: Int, Double, String and a lot more types are Hashable
    let id: Int

    let country: String
    let state: String
    let town: String
}

struct ContentView: View {
    // let’s make our variable a @State variable so that as soon as we change its value (by for eexample adding new elements) our view updates automagically.
    @State var locationList = [
    Location(id: 0, country: "Italy", state: "", town: "Finale Emilia"),
    Location(id: 1, country: "Italy", state: "", town: "Bologna"),
    Location(id: 2, country: "Italy", state: "", town: "Modena"),
    Location(id: 3, country: "Italy", state: "", town: "Reggio Emilia"),
    Location(id: 4, country: "USA", state: "CA", town: "Los Angeles")
    ]

    // - Constants
    private let locationManager = LocationManager()

    // THIS IS NOT POSSIBLE WITH SWIFTUI AND GENERATES ERRORS
    guard let exposedLocation = self.locationManager.exposedLocation else {
        print("*** Error in \(#function): exposedLocation is nil")
        return
    }

    var body: some View {
        // Whenever we use a List based of an Array we have to let the List know how to identify each row as unique
        // When confirming to the Identifiable protocol we no longer have to explicitly tell the List how the elements in our Array (which are conforming to that protocol) are uniquely identified
    NavigationView {
        // let’s add a title to our Navigation view and make sure you always do so on the first child view inside of your Navigation view
        List(locationList) { location in
            NavigationLink(destination: LocationDetail(location: location)) {
                HStack {
                   Text(location.country)
                   Text(location.town).foregroundColor(.blue)
                }
        }
    }
    .navigationBarTitle(Text("Location"))
    .navigationBarItems(
        trailing: Button(action: addLocation, label: { Text("Add") }))
    }
}

    func addLocation() {
      // We are using the native .randomElement() function of an Array to get a random element. The returned element however is optional. That is because in the case of the Array being empty that function would return nil. That’s why we append the returned value only in the case it doesn’t return nil.
      if let randomLocation = locationList.randomElement() {
        locationList.append(randomLocation)
      }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


推荐答案

您可以创建 LocationManager ObservedObject $ c>通过实现 ObservableObject 协议。

You could create an ObservedObject of your LocationManager by implementing the ObservableObject protocol.

通过 @Published 属性,您可以创建一个发布者对象,当该对象内部发生某些更改时,该对象将通知观察者(在这种情况下,是您的视图)。

With the @Published attribute you can create a publisher object which notify the observers (your view, in this case) when something changes inside that object.

这就是为什么在我的LocationManager中我在这些变量中添加了 @Published 属性:

That's why in my LocationManager I added the @Published attribute to those var:


  1. locationStatus:CLAuthorizationStatus?它包含从 didChangeAuthorization 委托方法
  2. 收到的值
  3. lastLocation: CLLocation?它包含由 didUpdateLocations 委托方法

  1. locationStatus: CLAuthorizationStatus? it contains the value received from didChangeAuthorization delegate method
  2. lastLocation: CLLocation? it contains the last location calculated by the didUpdateLocations delegate method


$计算的最后一个位置b $ b

LocationManager



LocationManager

import Foundation
import CoreLocation
import Combine

class LocationManager: NSObject, ObservableObject {

    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }

    @Published var locationStatus: CLAuthorizationStatus? {
        willSet {
            objectWillChange.send()
        }
    }

    @Published var lastLocation: CLLocation? {
        willSet {
            objectWillChange.send()
        }
    }

    var statusString: String {
        guard let status = locationStatus else {
            return "unknown"
        }

        switch status {
        case .notDetermined: return "notDetermined"
        case .authorizedWhenInUse: return "authorizedWhenInUse"
        case .authorizedAlways: return "authorizedAlways"
        case .restricted: return "restricted"
        case .denied: return "denied"
        default: return "unknown"
        }

    }

    let objectWillChange = PassthroughSubject<Void, Never>()

    private let locationManager = CLLocationManager()
}

extension LocationManager: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        self.locationStatus = status
        print(#function, statusString)
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.lastLocation = location
        print(#function, location)
    }

}



视图



在您的视图中,您只需要创建<$ c $的实例c> LocationManager 标记为 @ObservedObject

import SwiftUI

struct MyView: View {

    @ObservedObject var locationManager = LocationManager()

    var userLatitude: String {
        return "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
    }

    var userLongitude: String {
        return "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
    }

    var body: some View {
        VStack {
            Text("location status: \(locationManager.statusString)")
            HStack {
                Text("latitude: \(userLatitude)")
                Text("longitude: \(userLongitude)")
            }
        }
    }
}

struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView()
    }
}

这篇关于如何在没有ViewControllers的情况下使用SwiftUI获取当前位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆