在Swift中查找纬度和经度的城市名称和国家

jos*_*hua 19 location latitude-longitude core-location ios swift

我正在研究Swift3中的应用程序,我有字母问题我无法找到它的答案.

我怎样才能根据纬度和经度知道城市名称和国家短名称?

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate{
    let locationManager = CLLocationManager()
    var latitude: Double = 0
    var longitude: Double = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        // For use when the app is open & in the background
        locationManager.requestAlwaysAuthorization()
        // For use when the app is open
        //locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
        locationManager.startUpdatingLocation()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.first {
            print(location.coordinate)
            latitude = location.coordinate.latitude
            longitude = location.coordinate.longitude
        }
    }
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if (status == CLAuthorizationStatus.denied){
            showLocationDisabledpopUp()
        }
    }
    func showLocationDisabledpopUp() {
        let alertController = UIAlertController(title: "Background Location Access  Disabled", message: "We need your location", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        let openAction = UIAlertAction(title: "Open Setting", style: .default) { (action) in
            if let url = URL(string: UIApplicationOpenSettingsURLString){
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            }
        }
        alertController.addAction(openAction)
        self.present(alertController, animated: true, completion: nil)
    }
}
Run Code Online (Sandbox Code Playgroud)

Leo*_*bus 28

您可以使用CLGeocoder reverseGeocodeLocation方法获取CLPlacemark,获取国家地区属性信息.请注意,它是一个异步方法,因此在获取该信息时需要向方法添加完成处理程序:

import UIKit
import MapKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

extension CLLocation {
    func fetchCityAndCountry(completion: @escaping (_ city: String?, _ country:  String?, _ error: Error?) -> ()) {
        CLGeocoder().reverseGeocodeLocation(self) { completion($0?.first?.locality, $0?.first?.country, $1) }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
location.fetchCityAndCountry { city, country, error in
    guard let city = city, let country = country, error == nil else { return }
    print(city + ", " + country)  // Rio de Janeiro, Brazil
}
Run Code Online (Sandbox Code Playgroud)


pom*_*nto 17

我建议将Google Maps API与您的项目集成.如果您这样做,您的任务可以通过Google提供的反向地理编码来实现.

此外,Google还有用于IOS开发的Google Maps SDK,这也值得考虑.

UPD:您可以在不将地图集成到项目中的情况下执行此操作.基于答案,您可以使用对Google API的http请求来实现这一目标.请求:

https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&key=API_KEY 
Run Code Online (Sandbox Code Playgroud)

将返回JSON包含所请求地点信息的对象,包括国家和城市名称.

顺便说一句,我强烈建议使用Alamofire在Swift中发出http请求.

  • 建议使用谷歌可能不是最佳答案(除非你对该应用程序有一些好的投资).在您仔细阅读T&C之前,Google API会给您一种自由的幻觉.还有使用限制,因此请小心使用它.如果你只是想学习,那么一定要探索谷歌API,但我建议首先学习苹果的'CoreLocation`因为它可以做几乎所有的谷歌(除了一些高级功能),但有一点点编码(这很有趣) )并且如果您有效地编码它是免费的.建议@LeoDabus回答如下. (6认同)

Kha*_*idi 10

您需要的是反向地理编码.正如您已经在顶部声明了一些属性.您需要添加CLGeocoderCLPlancemark

let locationManager = CLLocationManager()
var location: CLLocation?

let geocoder = CLGeocoder()
var placemark: CLPlacemark?

// here I am declaring the iVars for city and country to access them later

var city: String?
var country: String?
var countryShortName: String?
Run Code Online (Sandbox Code Playgroud)

创建一个可以启动位置服务的功能

func startLocationManager() {
    // always good habit to check if locationServicesEnabled
    if CLLocationManager.locationServicesEnabled() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }
}
Run Code Online (Sandbox Code Playgroud)

完成位置地理编码后,还要创建另一个停止

func stopLocationManager() {
   locationManager.stopUpdatingLocation()
   locationManager.delegate = nil
}
Run Code Online (Sandbox Code Playgroud)

在视图didLoad或您想要启动位置管理器的任何地方首先添加一个检查

override func viewDidLoad() {
super.viewDidLoad()

    let authStatus = CLLocationManager.authorizationStatus()
    if authStatus == .notDetermined {
        locationManager.requestWhenInUseAuthorization()
    }

    if authStatus == .denied || authStatus == .restricted {
        // add any alert or inform the user to to enable location services 
    }

   // here you can call the start location function
   startLocationManager()

}
Run Code Online (Sandbox Code Playgroud)

实现位置管理器didFailedWithError的委托方法

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    // print the error to see what went wrong
    print("didFailwithError\(error)")
    // stop location manager if failed
    stopLocationManager()
}
Run Code Online (Sandbox Code Playgroud)

实现位置管理器didUpdateLocations的委托方法

 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    // if you need to get latest data you can get locations.last to check it if the device has been moved
    let latestLocation = locations.last!

    // here check if no need to continue just return still in the same place
    if latestLocation.horizontalAccuracy < 0 {
        return
    }
    // if it location is nil or it has been moved
    if location == nil || location!.horizontalAccuracy > lastLocation.horizontalAccuracy {

        location = lastLocation
        // stop location manager
        stopLocationManager()

        // Here is the place you want to start reverseGeocoding
        geocoder.reverseGeocodeLocation(lastLocation, completionHandler: { (placemarks, error) in
                // always good to check if no error
                // also we have to unwrap the placemark because it's optional
                // I have done all in a single if but you check them separately 
                if error == nil, let placemark = placemarks, !placemark.isEmpty {
                    self.placemark = placemark.last
                }
                // a new function where you start to parse placemarks to get the information you need
                self.parsePlacemarks()

           })
    }
}
Run Code Online (Sandbox Code Playgroud)

添加parsePlacemarks函数

parsePlacemarks() {
   // here we check if location manager is not nil using a _ wild card 
   if let _ = location {
        // unwrap the placemark 
        if let placemark = placemark {
            // wow now you can get the city name. remember that apple refers to city name as locality not city
            // again we have to unwrap the locality remember optionalllls also some times there is no text so we check that it should not be empty
            if let city = placemark.locality, !city.isEmpty {
                // here you have the city name
                // assign city name to our iVar
                self.city = city
            }
            // the same story optionalllls also they are not empty
            if let country = placemark.country, !country.isEmpty {

                self.country = country
            }
            // get the country short name which is called isoCountryCode
            if let countryShortName = placemark.isoCountryCode, !countryShortName.isEmpty {

                self.countryShortName = countryShortName
            }

        }


    } else {
       // add some more check's if for some reason location manager is nil
    }

}
Run Code Online (Sandbox Code Playgroud)

您必须cmd +单击CLPlacemark以查看您可以访问的所有属性,例如街道名称称为通道,并且该号码称为subThoroughfare继续阅读文档以获取更多信息

注意:您必须检查位置错误还有地理编码器错误,我在这里没有实现,但您必须处理这些错误和检查错误代码的最佳位置,其他一切都是苹果文档

更新:检查paresPlacemarks函数,其中我添加了isoCountryCode,它等于country shortName当你已经在使用位置服务时,不需要为google API和Alamofire添加额外的网络调用


Man*_*mam 6

这是Swift 4代码:

  var locationManager = CLLocationManager()

  override func viewDidLoad() {
    super.viewDidLoad()
    locationManager.delegate = self
    locationManager.requestWhenInUseAuthorization()
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.startUpdatingLocation()
    locationManager.startMonitoringSignificantLocationChanges()
    // Here you can check whether you have allowed the permission or not.
    if CLLocationManager.locationServicesEnabled()
    {
        switch(CLLocationManager.authorizationStatus())
        {
        case .authorizedAlways, .authorizedWhenInUse:
            print("Authorize.")
            let latitude: CLLocationDegrees = (locationManager.location?.coordinate.latitude)!
            let longitude: CLLocationDegrees = (locationManager.location?.coordinate.longitude)!
            let location = CLLocation(latitude: latitude, longitude: longitude) //changed!!!
            CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
                if error != nil {
                    return
                }else if let country = placemarks?.first?.country,
                    let city = placemarks?.first?.locality {
                    print(country)
                    self.cityNameStr = city
                }
                else {
                }
            })
            break

        case .notDetermined:
            print("Not determined.")
            self.showAlertMessage(messageTitle: "Bolo Board", withMessage: "Location service is disabled!!")
            break

        case .restricted:
            print("Restricted.")
            self.showAlertMessage(messageTitle: "Bolo Board", withMessage: "Location service is disabled!!")
            break

        case .denied:
            print("Denied.")
        }
    }
}

func showAlertMessage(messageTitle: NSString, withMessage: NSString) ->Void  {
    let alertController = UIAlertController(title: messageTitle as String, message: withMessage as String, preferredStyle: .alert)
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action:UIAlertAction!) in

    }
    alertController.addAction(cancelAction)

    let OKAction = UIAlertAction(title: "Settings", style: .default) { (action:UIAlertAction!) in
        if let url = URL(string: "App-Prefs:root=Privacy&path=LOCATION/com.company.AppName") {
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                // Fallback on earlier versions
            }
        }
    }
    alertController.addAction(OKAction)
    self.present(alertController, animated: true, completion:nil)
}
Run Code Online (Sandbox Code Playgroud)


Dil*_*hra 6

import Foundation
import CoreLocation

let location = CLLocation(latitude: 37.3321, longitude: -122.0318)
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in

    guard let placemark = placemarks?.first else {
        let errorString = error?.localizedDescription ?? "Unexpected Error"
        print("Unable to reverse geocode the given location. Error: \(errorString)")
        return
    }

    let reversedGeoLocation = ReversedGeoLocation(with: placemark)
    print(reversedGeoLocation.formattedAddress)
    // Apple Inc.,
    // 1 Infinite Loop,
    // Cupertino, CA 95014
    // United States
}

struct ReversedGeoLocation {
    let name: String            // eg. Apple Inc.
    let streetName: String      // eg. Infinite Loop
    let streetNumber: String    // eg. 1
    let city: String            // eg. Cupertino
    let state: String           // eg. CA
    let zipCode: String         // eg. 95014
    let country: String         // eg. United States
    let isoCountryCode: String  // eg. US

    var formattedAddress: String {
        return """
        \(name),
        \(streetNumber) \(streetName),
        \(city), \(state) \(zipCode)
        \(country)
        """
    }

    // Handle optionals as needed
    init(with placemark: CLPlacemark) {
        self.name           = placemark.name ?? ""
        self.streetName     = placemark.thoroughfare ?? ""
        self.streetNumber   = placemark.subThoroughfare ?? ""
        self.city           = placemark.locality ?? ""
        self.state          = placemark.administrativeArea ?? ""
        self.zipCode        = placemark.postalCode ?? ""
        self.country        = placemark.country ?? ""
        self.isoCountryCode = placemark.isoCountryCode ?? ""
    }
}
Run Code Online (Sandbox Code Playgroud)


Pau*_*tos 5

CLGeocoder为此,您可以使用CoreLocation 中的 ,​​ 。来自苹果文档(强调我的):

用于在地理坐标和地名之间进行转换的单镜头对象。

该类CLGeocoder提供在坐标(指定为纬度和经度)和该坐标的用户友好表示形式之间进行转换的服务。用户友好的坐标表示通常包括与给定位置相对应的街道、城市、州和国家/地区信息......

此服务与MapKit无关,因此根本不需要您在应用程序中使用/显示地图。