【发布时间】:2012-01-21 23:58:57
【问题描述】:
我需要获取 iOS 设备的国家/地区位置。
我一直在尝试将 CoreLocation 与 MKReverseGeocoder 一起使用。然而,这似乎经常返回错误。而且我只需要国家,不需要街道之类的。
如何以更稳定的方式做到这一点?
【问题讨论】:
标签: ios core-location
我需要获取 iOS 设备的国家/地区位置。
我一直在尝试将 CoreLocation 与 MKReverseGeocoder 一起使用。然而,这似乎经常返回错误。而且我只需要国家,不需要街道之类的。
如何以更稳定的方式做到这一点?
【问题讨论】:
标签: ios core-location
NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
会给你一个标识符,例如“US”(美国)、“ES”(西班牙)等
在 Swift 3 中:
let countryCode = NSLocale.current.regionCode
在 Swift 2.2 中:
let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as String
与基于 CLLocationManager 的解决方案相比,这种方法有利有弊。主要缺点是,如果用户配置不同,它不能保证这是设备的物理位置。然而,这也可以被视为专业人士,因为它反而显示了用户在心理/文化上与哪个国家一致 - 例如,如果我出国度假,然后语言环境仍然设置为我的祖国。然而,一个相当大的专业人士是这个 API 不需要像 CLLocationManager 那样的用户权限。因此,如果您尚未获得使用用户位置的许可,并且您不能真正证明在用户面前抛出一个弹出对话框是合理的(或者他们已经拒绝了该弹出窗口并且您需要后备),那么这可能是您的 API想用。这方面的一些典型用例可能是个性化(例如文化相关内容、默认格式等)和分析。
【讨论】:
NSLocale 只是关于当前使用的区域设置的设置,并不代表您所在的实际国家/地区。
使用CLLocationManager 获取当前位置并使用CLGeocoder 执行反向地理编码。您可以从那里获取国家/地区名称。
【讨论】:
@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]);
}];
}
【讨论】:
这里是@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")
}
}
}
不要忘记在Info.plist 中设置NSLocationAlwaysUsageDescription 或NSLocationWhenInUseUsageDescription!
【讨论】:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) 我将更新原始帖子以澄清这一点
正如@Denis Locale 所提到的,这只是关于当前使用的区域设置的设置,并不意味着您所在的实际国家/地区。
但是,建议使用CLLocationManager 获取当前位置并使用CLGeocoder 执行反向地理编码,这意味着提示用户访问位置服务。
如何从移动运营商获取国家代码?
import CoreTelephony
guard carrier = CTTelephonyNetworkInfo().subscriberCellularProvider else {
//iPad
return
}
let countryST = carrier.isoCountryCode!
【讨论】:
这是另一种可能过于迂回的方法。其他解决方案基于手动设置(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']
...
iOS 端:
NSTimeZone *tz = [NSTimeZone localTimeZone];
DLog(@"Local timezone: %@", tz.name); // prints "America/Los_Angeles"
我让我的服务器发送本地时区名称并在 pytz country_timezones 字典中查找它。
如果您在 pytz 或其他来源中提供 iOS 版本的字典,您可以使用它来立即查找国家/地区代码,而无需服务器的帮助,基于时区设置,这些设置通常是最新的。
我可能误解了 NSLocale。它是否通过区域格式首选项或时区设置为您提供国家代码?如果是后者,那么这只是获得相同结果的更复杂的方法......
【讨论】:
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
【讨论】:
对于 Swift 3,它更简单:
let countryCode = Locale.current.regionCode
【讨论】:
根据地区集获取国家/地区名称的 Swift 4.0 代码:
let countryLocale = NSLocale.current
let countryCode = countryLocale.regionCode
let country = (countryLocale as NSLocale).displayName(forKey: NSLocale.Key.countryCode, value: countryCode)
print(countryCode, country)
打印:可选(“NG”)可选(“尼日利亚”)。 //对于尼日利亚区域集
【讨论】:
如果 Locale.current.regionCode 由于某些原因不起作用,请使用 CoreTelephony 方法作为备用方法。
import CoreTelephony
if let carriers = CTTelephonyNetworkInfo().serviceSubscriberCellularProviders?.values, let countryCode = Array(carriers).compactMap { $0.isoCountryCode }.first {
print("❤️ \(countryCode)")
}
【讨论】:
您可以从 CLLocation:https://github.com/Alterplay/APTimeZones 获取 NSTimeZone 并在本地工作。
【讨论】:
如果你只对电话设备感兴趣,那么这里提到的技术可能对你有用:Determine iPhone user's country
【讨论】:
这是 Swift 3 中的一个快速循环,它返回完整的国家代码列表。
let countryCode = NSLocale.isoCountryCodes
for country in countryCode {
print(country)
}
【讨论】:
@Rob
let locale = Locale.current
let code = (locale as NSLocale).object(forKey: NSLocale.Key.countryCode) as! String?
使用这些代码,您将获得您所在地区的国家/地区代码,如果您还没有得到静止,那么只需进入电话设置->通用->语言和地区并设置您想要的地区
【讨论】:
if let countryCode = Locale.current.regionCode {
let country = Locale.current.localizedString(forRegionCode: countryCode)
}
【讨论】: