【iOS】7.4 定位服务->2.1.3.2 定位 - 官方框架CoreLocation 功能2:地理编码和反地理编码
[blockquote]
本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正。
[/blockquote]
[blockquote]
本文相关目录: ================== 所属文集:【iOS】07 设备工具 ================== 7.4 定位服务->1.0 简介 7.4 定位服务->2.1.1 定位 - 官方框架CoreLocation: 请求用户授权 7.4 定位服务->2.1.2 定位 - 官方框架CoreLocation: CLLocationManager位置管理器 7.4 定位服务->2.1.3.1 定位 - 官方框架CoreLocation 功能1:地理定位 7.4 定位服务->2.1.3.2 定位 - 官方框架CoreLocation 功能2:地理编码和反地理编码 7.4 定位服务->2.1.3.3 定位 - 官方框架CoreLocation 功能3:区域监听 7.4 定位服务->2.1.4 定位 - 官方框架CoreLocation 案例:指南针效果 7.4 定位服务->2.2 定位 - locationManager框架 7.4 定位服务->3.1 地图框架MapKit 功能1:地图展示 7.4 定位服务->3.2 地图框架MapKit 功能2:路线规划(导航) 7.4 定位服务->3.3 地图框架MapKit 功能3:3D视图 7.4 定位服务->3.4 地图框架MapKit 功能4:地图截图 7.4 定位服务->3.5 地图框架MapKit 功能5:POI检索 ================== 所属文集:【iOS】07 设备工具 ==================
[/blockquote]
定位目录:
官方框架CoreLocation目录:
定位的功能实现:
本文目录:
1.0 概念解释
2.0 相关方法
下面介绍下CLPlacemark对象相关的知识:
3.0 实现步骤
代码5:
编译环境:Xcode 8.0 模拟器版本:iOS 10 Swift版本:3.0
【OC 语言】
#import "ViewController.h" #import <CoreLocation/CoreLocation.h> #import <AddressBook/AddressBook.h> @interface ViewController () @property (weak, nonatomic) IBOutlet UITextView *addressTextView;// 地址 @property (weak, nonatomic) IBOutlet UITextField *latitudeTextField;// 纬度 @property (weak, nonatomic) IBOutlet UITextField *longitudeUITextField;// 经度 @property(nonatomic, strong) CLGeocoder *geoCoder; // 用作地理编码、反地理编码的工具类 @end @implementation ViewController #pragma mark - 懒加载 - (CLGeocoder *)geoCoder { if (!_geoCoder) { _geoCoder = [[CLGeocoder alloc] init]; } return _geoCoder; } #pragma mark - 地理编码(地址->经纬度) - (IBAction)geoCode:(UIButton *)sender { // 判断输入地址的 text 是否为空 if ([self.addressTextView.text length] == 0) { return; } #pragma mark - 地理编码方案1:根据地址字符串解析,进行地理编码(返回结果可能有多个,因为一个地点有重名) [self.geoCoder geocodeAddressString:self.addressTextView.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { // 防错处理 if (error || placemarks.count == 0) { NSLog(@"地理编码错误:%@", error); return; } // 罗列出所有搜索到的地址名称 for (CLPlacemark *placemark in placemarks) { NSLog(@"所有搜索到的地址名称:%@",placemark.name); } // 一个placemark对应一个地理坐标,包含一个地方的信息(因为可能有多种结果,所有是一个数组,因此要给用户一个列表去选择) CLPlacemark *placemark = [placemarks firstObject]; // 包含区,街道等信息的地标对象 NSLog(@"搜索到的第一个城市名:%@,搜索到的第一个地址名称:%@", placemark.locality, placemark.name); // 显示搜索到的第一个地址名称,到 addressTextView 中 self.addressTextView.text = [NSString stringWithFormat:@"%@", placemark.name]; // 显示纬度(拼接字符串),到 latitudeTextField self.latitudeTextField.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude]; // 显示经度(拼接字符串),到 longitudeUITextField self.longitudeUITextField.text =[NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude]; }]; #pragma mark - 地理编码方案2:在区域内根据地址字符串解析,进行地理编码(更加精确) [self.geoCoder geocodeAddressString:self.addressTextView.text inRegion:nil completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { // 防错处理 if (error || placemarks.count == 0) { NSLog(@"地理编码错误:%@", error); return; } // 罗列出所有搜索到的地址名称 for (CLPlacemark *placemark in placemarks) { NSLog(@"所有搜索到的地址名称:%@",placemark.name); } // 一个placemark对应一个地理坐标,包含一个地方的信息(因为可能有多种结果,所有是一个数组,因此要给用户一个列表去选择) CLPlacemark *placemark = [placemarks firstObject]; // 包含区,街道等信息的地标对象 NSLog(@"搜索到的第一个城市名:%@,搜索到的第一个地址名称:%@", placemark.locality, placemark.name); // 显示搜索到的第一个地址名称,到 addressTextView 中 self.addressTextView.text = [NSString stringWithFormat:@"%@", placemark.name]; // 显示纬度(拼接字符串),到 latitudeTextField self.latitudeTextField.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude]; // 显示经度(拼接字符串),到 longitudeUITextField self.longitudeUITextField.text =[NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude]; }]; #pragma mark - 地理编码方案3:根据地址信息字典解析,进行地理编码(不推荐) NSDictionary *addressDic = @{ (__bridge NSString *)kABPersonAddressStreetKey:@"杭州",(__bridge NSString *)kABPersonAddressStreetKey:@"上海" }; [self.geoCoder geocodeAddressDictionary:addressDic completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { // code }]; } #pragma mark - 清空 - (IBAction)clearButton:(UIButton *)sender { self.addressTextView.text = NULL; self.latitudeTextField.text = NULL; self.longitudeUITextField.text = NULL; } #pragma mark - 反地理编码:根据给定的经纬度,获得具体的位置信息 - (IBAction)reverseGeoCode:(UIButton *)sender { // 过滤空数据 if ([self.latitudeTextField.text length] == 0 || [self.longitudeUITextField.text length] == 0) { return; } // 创建CLLocation对象 double latitude = [self.latitudeTextField.text doubleValue]; double longitude = [self.longitudeUITextField.text doubleValue]; CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; // 根据CLLocation对象进行反地理编码 [self.geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { // 防错处理 if (error || placemarks.count == 0) { NSLog(@"反地理编码错误:%@", error); return; } // 罗列出所有搜索到的地址名称 for (CLPlacemark *placemark in placemarks) { NSLog(@"所有搜索到的地址名称:%@",placemark.name); } // 一个placemark对应一个地理坐标,包含一个地方的信息(因为可能有多种结果,所有是一个数组,因此要给用户一个列表去选择) CLPlacemark *placemark = [placemarks firstObject]; // 包含区,街道等信息的地标对象 NSLog(@"搜索到的第一个城市名:%@,搜索到的第一个地址名称:%@", placemark.locality, placemark.name); // 显示地址方法1: // for (CLPlacemark *placemark in placemarks) { // self.addressTextView.text = placemark.name; //具体地名 // } // 显示地址方法2:显示搜索到的第一个地址名称,到 addressTextView 中 self.addressTextView.text = [NSString stringWithFormat:@"%@", placemark.name]; // 显示纬度(拼接字符串),到 latitudeTextField self.latitudeTextField.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude]; // 显示经度(拼接字符串),到 longitudeUITextField self.longitudeUITextField.text =[NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude]; }]; } #pragma mark - 退出键盘 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self.view endEditing:YES]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
运行效果:
打印结果:
OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:河南省信阳市罗山县五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:河南省许昌市长葛市五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:内蒙古自治区巴彦淖尔市乌拉特前旗五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:山西省临汾市尧都区五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:辽宁省鞍山市岫岩满族自治县五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:山西省临汾市尧都区五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:山西省太原市晋源区五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:北京市海淀区五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:福建省泉州市德化县五道口 OC - 地理编码和反地理编码[16739:561135] 搜索到的第一个城市名:信阳市,搜索到的第一个地址名称:河南省信阳市罗山县五道口 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:北京市 OC - 地理编码和反地理编码[16739:561135] 搜索到的第一个城市名:北京市,搜索到的第一个地址名称:北京市 OC - 地理编码和反地理编码[16739:561135] 所有搜索到的地址名称:甘棠镇 OC - 地理编码和反地理编码[16739:561135] 搜索到的第一个城市名:南宁市,搜索到的第一个地址名称:甘棠镇
【Swift语言】
import UIKit import CoreLocation class ViewController: UIViewController { @IBOutlet weak var addressTextView: UITextView! // 地址 @IBOutlet weak var latitudeTextField: UITextField! // 纬度 @IBOutlet weak var longitudeUITextField: UITextField! // 经度 // MARK: - 懒加载 lazy var geoCoder : CLGeocoder = { return CLGeocoder() }() // MARK: - 地理编码(地址->经纬度) @IBAction func geoCode(_ sender: UIButton) { let address = addressTextView.text geoCoder.geocodeAddressString(address!) { (placemarks : [CLPlacemark]?, error :Error?) in if error == nil{ print("地理编码成功") guard let pls = placemarks else {return} let firstPL = pls.first self.addressTextView.text = firstPL?.name self.latitudeTextField.text = "\(firstPL?.location?.coordinate.latitude)" self.longitudeUITextField.text = "\(firstPL?.location?.coordinate.longitude)" }else { print("地理编码错误!!!") } } } // MARK: - 清空 @IBAction func clearButton(_ sender: UIButton) { self.addressTextView.text = nil self.latitudeTextField.text = nil self.longitudeUITextField.text = nil } // MARK: - 反地理编码(经纬度->地址) @IBAction func reverseGeoCode(_ sender: UIButton) { let latitude = CLLocationDegrees(latitudeTextField.text!) let longitude = CLLocationDegrees(longitudeUITextField.text!) let location = CLLocation(latitude: latitude!, longitude: longitude!) geoCoder.reverseGeocodeLocation(location) { (placemarks :[CLPlacemark]?, error :Error?) in if error == nil{ print("反地理编码成功") guard let pls = placemarks else {return} let firstPL = pls.first self.addressTextView.text = firstPL?.name self.latitudeTextField.text = "\(firstPL?.location?.coordinate.latitude)" self.longitudeUITextField.text = "\(firstPL?.location?.coordinate.longitude)" }else { print("反地理编码错误!!!") } } } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
注:运行效果和打印结果省略
4.0 案例:获取当前位置名称
代码6:
编译环境:Xcode 8.0 模拟器版本:iOS 10 Swift版本:3.0
【OC 语言】
暂无,后期补上
【Swift语言】
import UIKit import CoreLocation class ViewController: UIViewController { // 用于记录上次位置数据的变量 var lastLoc: CLLocation? // 位置管理者的懒加载 lazy var locationM : CLLocationManager = { let locationM : CLLocationManager = CLLocationManager() // 创建 locationM.delegate = self // 遵守代理 // 如果在iOS8.0之后, 需要额外的执行以下代码, 主动请求用户授权 if #available(iOS 8.0, *) { // 请求前台定位授权(不要忘记在info.plist文件中, 配置对应的key) locationM.requestWhenInUseAuthorization() // 如果是iOS9.0之后, 当前授权状态是前台定位授权状态, 也想在后台获取用户的位置信息,那么需要满足以下条件 // 1. 勾选后台模式 2. 设置以下属性为true(OC, 里面是YES) if #available(iOS 9.0, *) { locationM.allowsBackgroundLocationUpdates = true } locationM.desiredAccuracy = kCLLocationAccuracyBest // 设置精确度 } return locationM }() // MARK: - 点击屏幕开始定位 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 定位: 标准定位服务 (gps/wifi/蓝牙/基站) locationM.startUpdatingLocation() } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } // MARK: - 代理 extension ViewController: CLLocationManagerDelegate { // 当获取到用户位置信息时调用 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 当前位置 guard let newLocation = locations.last else {return} // 容错判断 if newLocation.horizontalAccuracy < 0 { return } // 场景演示:打印当前用户的行走方向,偏离角度以及对应的行走距离 // 例如:”北偏东 30度 方向,移动了 8米” // 实现步骤: // 1> 获取对应的方向偏向(例如”正东”,”东偏南”) // 2> 获取对应的偏离角度(并判断是否是正方向) // 3> 计算行走距离 // 4> 打印信息 // 1. 获取当前的行走航向 (算法: 根据航向course, 整除90, 判断出对应的方位) let courseStrs : [String] = ["北偏东", "东偏南", "南偏西", "西偏北"] // 定义数组,各个值为0,1,2,3 let index : Int = Int(newLocation.course) / 90 // 将当前位置/90所得即是数组索引 var courseStr : String = courseStrs[index] // 当前的方向 // 2. 行走的偏离角度 let courseAngle : Int = Int(newLocation.course) % 90 // 取余 // 如果能整除,就是正方向 if Int(courseAngle) == 0 { let index = courseStr.characters.index(courseStr.startIndex, offsetBy: 1) // 截取数组的字符串的第1个 courseStr = "正" + courseStr.substring(to: index) // 拼接(正x方向) } // 3. 移动了多少米(计算当前位置和上一次的位置的距离) let lastLocation = lastLoc ?? newLocation let distance = newLocation.distance(from: lastLocation) lastLoc = newLocation // 4. 合并字符串, 打印( 例如:”北偏东 30度 方向,移动了 8米” ) if courseAngle == 0{ print("正\(courseStr)方向,移动了\(distance)米") }else{ print("\(courseStr)\(courseAngle)度方向,移动了\(distance)米") } } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("定位失败") } }
打印结果:
南偏西78度方向,移动了0.0米 南偏西78度方向,移动了0.0米 南偏西78度方向,移动了0.0米 南偏西4度方向,移动了43.5456733705825米 东偏南85度方向,移动了4.16083969694213米 南偏西5度方向,移动了4.19352559351701米 南偏西1度方向,移动了3.98972251112523米 东偏南88度方向,移动了3.87999866791506米 东偏南87度方向,移动了3.84349812602442米 东偏南82度方向,移动了3.82326469279695米 东偏南79度方向,移动了3.79777809972005米
[blockquote]
本文源码 Demo 详见 Github https://github.com/shorfng/iOS_7.0_Device-Tools
[/blockquote]
[blockquote]
作者:蓝田(Loto)
[/blockquote]
【作品发布平台】
① 简书 ② 安科开发 ③ Gitbook(如果觉得文章太长,请阅读此平台发布的文章)
【代码托管平台】
Github
【如有疑问,请通过以下方式交流】
① 评论区回复
② 发送邮件
至 [email protected]
本文版权归作者和本网站共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
支付宝扫一扫 向我打赏
你也可以微信 向我打赏