在iOS中获取设备位置(仅限国家/地区)

joh*_*han 59 core-location ios

我需要获取iOS设备的国家/地区位置.

我一直在尝试将CoreLocation与MKReverseGeocoder一起使用.然而,这似乎经常发生错误.我只需要这个国家,不需要街道等.

如何以更稳定的方式完成?

Mar*_*aek 95

NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
Run Code Online (Sandbox Code Playgroud)

会给你一个标识符,例如"US"(美国),"ES"(西班牙)等.


Swift 3中:

let countryCode = NSLocale.current.regionCode
Run Code Online (Sandbox Code Playgroud)

Swift 2.2中:

let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as String
Run Code Online (Sandbox Code Playgroud)

与基于CLLocationManager的解决方案相比,这种方法有利有弊.主要的缺点是,如果用户以不同方式配置设备,则无法保证这是设备的物理位置.然而,这可以被视为专业人士,因为它反而显示用户在精神上/文化上与哪个国家保持一致 - 因此,如果我出国度假,那么该地区仍然设置在我的祖国.然而,一个非常大的专业人士是这个API不需要像CLLocationManager那样的用户权限.因此,如果你还没有获得使用用户位置的权限,并且你无法证明在用户面前抛出弹出对话框(或者他们已经拒绝了弹出窗口而你需要回退),那么这可能是你的API想用.一些典型的用例可能是个性化(例如文化相关内容,默认格式等)和分析.

  • 在iPhone上,转到设置 - >常规 - >国际.在那里,您可以分别更改语言,区域格式(区域设置的用户友好术语)和日历(格里高利,日本或佛教).不同的组合是可能的 - 例如我作为一个丹麦人仍然喜欢英语界面,但丹麦语言环境(因此星期一星期一开始,我想要dd/mm/yy,因为mm/dd/yy很奇怪;)这不是受设备物理位置的影响(我不希望它突然变成意大利语,因为我正在度假......) (10认同)
  • 这与地点无关,只与语言有关。 (2认同)

Den*_*nis 41

NSLocale 只是关于当前使用的区域设置的设置,它并不意味着您所在的实际国家/地区.

使用CLLocationManager来获取当前位置及CLGeocoder执行反向地理编码.你可以从那里获得国家名称.


Mat*_*att 15

@Denis的答案很好 - 这里有一些代码将他的答案付诸实践.这适用于您已设置为符合CLLocationManagerDelegate协议的自定义类.它有点简化(例如,如果位置管理器返回多个位置,它只是与第一个位置一起),但应该给人们一个不错的开始......

- (id) init //designated initializer
{
    if (self)
    {
        self.locationManager = [[CLLocationManager alloc] init];
        self.geocoder = [[CLGeocoder alloc] init];
        self.locationManager.delegate = self;
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
    return self;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    if (locations == nil)
        return;

    self.currentLocation = [locations objectAtIndex:0];
    [self.geocoder reverseGeocodeLocation:self.currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
    {
        if (placemarks == nil)
            return;

        self.currentLocPlacemark = [placemarks objectAtIndex:0];
        NSLog(@"Current country: %@", [self.currentLocPlacemark country]);
        NSLog(@"Current country code: %@", [self.currentLocPlacemark ISOcountryCode]);
    }];
}
Run Code Online (Sandbox Code Playgroud)


小智 11

这是@Denis和@Matt的答案,为Swift 3解决方案拼凑而成:

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager = CLLocationManager()
    let geoCoder = CLGeocoder()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.requestAlwaysAuthorization()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.startMonitoringSignificantLocationChanges()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let currentLocation = locations.first else { return }

        geoCoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in
            guard let currentLocPlacemark = placemarks?.first else { return }
            print(currentLocPlacemark.country ?? "No country found")
            print(currentLocPlacemark.isoCountryCode ?? "No country code found")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

别忘了设置NSLocationAlwaysUsageDescription或者NSLocationWhenInUseUsageDescription进入Info.plist!


小智 8

正如@Denis 所提到的,这Locale只是关于当前使用的区域设置的设置,并不意味着您所在的实际国家/地区。

但是,建议使用CLLocationManager来获取当前位置并CLGeocoder执行反向地理编码,这意味着提示用户访问位置服务。

如何从移动运营商获取国家代码?

import CoreTelephony

guard carrier = CTTelephonyNetworkInfo().subscriberCellularProvider else {
    //iPad
    return
}

let countryST = carrier.isoCountryCode!
Run Code Online (Sandbox Code Playgroud)


sco*_*ter 7

这是另一种可能过于迂回的方法.其他解决方案基于手动设置(NSLocale)或请求允许使用可拒绝的位置服务(CLLocationManager),因此它们有缺点.

您可以根据当地时区获取当前国家/地区.我的应用程序与安装了pytz的运行Python的服务器连接,该模块为时区字符串提供了国家代码字典.我只需要让服务器知道这个国家,所以我不必完全在iOS上进行设置.在Python方面:

>>> import pytz
>>> for country, timezones in pytz.country_timezones.items():
...     print country, timezones
... 
BD ['Asia/Dhaka']
BE ['Europe/Brussels']
BF ['Africa/Ouagadougou']
BG ['Europe/Sofia']
BA ['Europe/Sarajevo']
BB ['America/Barbados']
WF ['Pacific/Wallis']
...
Run Code Online (Sandbox Code Playgroud)

在iOS方面:

NSTimeZone *tz = [NSTimeZone localTimeZone];
DLog(@"Local timezone: %@", tz.name); // prints "America/Los_Angeles"
Run Code Online (Sandbox Code Playgroud)

我让我的服务器以本地时区名称发送,并在pytz country_timezones字典中查找.

如果您在pytz或其他来源中提供字典的iOS版本,则可以使用它在没有服务器帮助的情况下立即查找国家/地区代码,具体取决于时区设置(通常是最新的).

我可能会误解NSLocale.它是否通过区域格式设置首选项或时区设置为您提供国家/地区代码?如果是后者,那么这只是获得相同结果的一种更复杂的方式......


Sof*_*ner 6

如果由于某些原因不起作用,请使用该CoreTelephony方法作为后备方法。Locale.current.regionCode

\n
import CoreTelephony\n\nif let carriers = CTTelephonyNetworkInfo().serviceSubscriberCellularProviders?.values, let countryCode = Array(carriers).compactMap { $0.isoCountryCode }.first {\n    print("\xe2\x9d\xa4\xef\xb8\x8f \\(countryCode)")\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Tej*_*ina 5

NSLocale *countryLocale = [NSLocale currentLocale];  
NSString *countryCode = [countryLocale objectForKey:NSLocaleCountryCode];
NSString *country = [countryLocale displayNameForKey:NSLocaleCountryCode value:countryCode];
NSLog(@"Country Locale:%@  Code:%@ Name:%@", countryLocale, countryCode, country);
//Country Locale:<__NSCFLocale: 0x7fd4b343ed40>  Code:US   Name:United States
Run Code Online (Sandbox Code Playgroud)