来一起学习下地图和定位的使用吧,如有不足,欢迎指正
一.定位功能
1.ios7中的定位
1.1 导入 CoreLocation框架
1.2 创建 CLLocationManager对象
注意:要用强指针指向这个对象,一般采用懒加载来创建
1 private lazy var mgr : CLLocationManager = CLLocationManager()
1.3 设置代理,实现代理方法
1.4 开始定位
mgr.startUpdatingLocation()
1.5 优点:不需要设置用户的授权
在info.plist加上一个key Privacy - Location Usage Description 写在value上的文字,可以显示在提示权限的文本框内
2.ios8(之后)的定位
2.1 请求定位步骤
2.11 导入CoreLocation框架
2.12 懒加载CLLocationManager对象
2.13 请求授权 (1) whenInUse (2) always
2.14 注意:必须把授权对应的key值 添加到info.plist文件中
2.15 设置代理,实现代理方法
2.16 开始定位
2.2 定位属性的应用
2.21 精确度的使用
desiredAccuracy精确度越高,越耗电属性接收double类型的值,不过最好传系统给定好的值kCLLocationAccuracyBestForNavigation: 导航精确度(最精确)
kCLLocationAccuracyBest: 最好精确度(默认)
kCLLocationAccuracyNearestTenMeters: 10米的误差
kCLLocationAccuracyHundredMeters: 100米的误差
kCLLocationAccuracyKilometer: 千米误差kCLLocationAccuracyThreeKilometers: 三千米的误差
mgr.desiredAccuracy = kCLLocationAccuracyBestForNavigation
2.22 移动一段距离,再次重新定位
设置用于移动多少距离,重新进行定位
mgr.distanceFilter = 100
2.3 位置信息的获取
2.31 发送完请求定位,怎么获取位置信息?
在代理方法的闭包中,有一个数组,返回了很多信息在里面
2.32 我们常用的信息就是经纬度
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908131628769-950179120.png)
二.计算两个经纬度的距离
1.获取当前位置信息
1.1 导入框架
1.2 懒加载管理者对象
1.3 请求授权
1.4 添加key值
1.5 设置代理,实现代理方法
1.6 开始定位
2.获取另一个位置的经纬度
3.计算两个位置的距离 distanceFromLocation
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908131707957-1110127167.png)
三.简易指南针的制作
1.实现思路
监听手机头方向的改变,在手机屏幕上放一张图片,始终指向北(根据手机方向的改变旋转)
2.界面搭建
拖一个UIImageView放在屏幕中央,里面放一张图片
3.监听手机头方向的改变
3.1 怎么监听?
通过发送请求(定位服务),获取手机头的方向进行监听
3.2 具体实现
3.21 导入CoreLocation框架
3.22 懒加载CLLocationManager对象
3.23 请求授权 (1) whenInUse (2) always
3.24 注意:必须把授权对应的key值 添加到info.plist文件中
3.25 设置代理,实现代理方法
3.26 请求手机头方向
3.27 获取真北方向
3.28 将真北方向转换为弧度
3.29 让图片根据弧度进行旋转(注意:图片旋转的弧度要取反 , 要和屏幕旋转方向相反才能保持一直指向一个方向)
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908131741629-706532751.png)
4.对指南针优化
4.1 真实的指南针指向一个位置,会来回摆动两下才固定位置
代码实现的指南针没有这个效果
4.2 如何实现这个效果?
可以通过一个动画来实现
// Damping : 阻力系数 (0~1.0) initialSpringVelocity:回弹速度 UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 5.0, options: [], animations: { self.imageView.transform = transform }, completion: nil)
四.区域监听
1.需求,当进入某指向区域,提醒用户进入该区域,离开该区域也对用户进行提醒
2.实现方案步骤
2.1 懒加载 CLLocationManager对象
2.2 请求授权(注意:必须使用alyays授权方式) ,配置info文件
2.3 设置代理
2.4 创建监听区域
2.5 实现代理方法 并 开始监听
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908131909035-968802555.png)
3.注意点
3.1 当之前添加过监听区域时,再次添加新的监听区域,还会对之前的区域进行监听
3.2 不想监听之前的区域,必须通过代码移除之前的区域
五.地理编码&反地理编码
需求:输入地理名称,地理编码获得该位置的经纬度. 输入经纬度,输出对应位置的地理名称
1.界面搭建
1.1 整个界面放在屏幕的中心,怎么实现?
1.11 可以用view包装
优缺点:要做大量的约束 , 但可以应用于任何版本
1.12 ios9之后可以用UIStackView来包装
优缺点:布局简便 只能适用于ios9(之后)
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908131933098-1289120007.png)
2.地理编码
2.1 拿到用户输入的地理名称 (导入框架CoreLocation)
2.2 地理编码
2.21 创建 CLGeocoder对象
2.22 对地理名称进行地理编码
geocoder.geocodeAddressString(address) { (<#[CLPlacemark]?#>, <#NSError?#>) in <#code#> }
2.23 对闭包中的CLPlacemark数据就行解析(遍历)
一个地理名称可能对应多个地方,所以编码后的到的结果是一个数组
2.24 获取数组中元素的地理位置(经纬度)
2.25 将经纬度显示到界面
3.反地理编码
3.1 拿到用户输入的经纬度
3.2 对经纬度进行反地理编码
3.21 创建 CLGeocoder对象
3.22 对经纬度进行反地理编码
geocoder.reverseGeocodeLocation(location) { (<#[CLPlacemark]?#>, <#NSError?#>) in <#code#> }
3.23 对闭包中的CLPlacemark数据就行解析(遍历)
一个经纬度可能对应多个位置(苹果这么设计的) 所以编码后返回一个数组
一个位置包含多个信息(省/市/街道/国家/经纬度/) 编码后的结果是字典数组
3.24 取出数组中的一个位置(字典),再获取位置信息(取出字典的元素)
3.25 把获取到的地理名称显示到界面
地理编码&反地理编码源代码
1 class ViewController: UIViewController { 2 3 // MARK:- 控件属性 4 @IBOutlet weak var addressTextView: UITextView! 5 @IBOutlet weak var latitudeTextField: UITextField! 6 @IBOutlet weak var longitudeTextField: UITextField! 7 // MARK:- 懒加载属性 8 private lazy var geocoder : CLGeocoder = CLGeocoder() 9 } 10 11 // MARK:- 地理编码 12 extension ViewController { 13 @IBAction func geocode() { 14 15 // 1.获取用户输入的地址名称 16 guard let address = addressTextView.text else { 17 return 18 } 19 // 2.对地理名称进行地理编码 20 geocoder.geocodeAddressString(address) { (placemarks : [CLPlacemark]?, error : NSError?) in 21 // 1.错误校验 22 if error != nil { 23 print(error) 24 return 25 } 26 // 2.对结果进行校验 27 guard let placemarks = placemarks else { 28 return 29 } 30 // 3.遍历所有的结果 31 for place in placemarks { 32 print(place.name) 33 34 // 获取地理位置 35 guard let location = place.location else { 36 continue 37 } 38 39 // 获取经纬度 40 let latitude = location.coordinate.latitude 41 let longitude = location.coordinate.longitude 42 43 // 将经纬度显示textField中 44 self.latitudeTextField.text = "(latitude)" 45 self.longitudeTextField.text = "(longitude)" 46 } 47 } 48 } 49 } 50 51 52 // MARK:- 反地理编码 53 extension ViewController { 54 @IBAction func reverseGeocode() { 55 // 1.获取用户输入的经纬度 56 guard let latitude = latitudeTextField.text, let longitude = longitudeTextField.text else { 57 return 58 } 59 60 // 2.将经纬度转成CLLocation对象 61 guard let latitudeD = Double(latitude), let longitudeD = Double(longitude) else { 62 return 63 } 64 let location = CLLocation(latitude: latitudeD, longitude: longitudeD) 65 66 // 3.反地理编码 67 geocoder.reverseGeocodeLocation(location) { (placemarks : [CLPlacemark]?, error : NSError?) in 68 // 1.错误校验 69 if error != nil { 70 print(error) 71 return 72 } 73 74 // 2.对结果进行校验 75 guard let placemarks = placemarks else { 76 return 77 } 78 // 3.遍历结果 79 for place in placemarks { 80 guard let addressDict = place.addressDictionary else { 81 continue 82 } 83 84 guard let addressArray = addressDict["FormattedAddressLines"] as? [String] else { 85 continue 86 } 87 88 guard let address = addressArray.last else { 89 continue 90 } 91 92 self.addressTextView.text = address 93 } 94 } 95 } 96 }
六.把定位封装为工具类
1.将工具类设计成单例对象
2.封装请求方法,在方法中传入闭包
2.1 使用属性将闭包保存起来. (因为在代理方法才能拿到位置信息)
2.2 请求用户位置(1.懒加载管理者对象,并在对象中直接设置请求授权和代理)
3.在代理方法中获得用户位置信息,并赋值给闭包属性
4.停止请求用户的位置
mgr.stopUpdatingLocation()
5.当第一次发送请求位置信息,会返回多次位置信息,怎么解决这个问题?
用户只需要定义一个Bool属性,对属性进行判断,为true就接收返回的位置信息
七.使用第三方框架请求位置信息
去github搜索LocationManager 找到框架去使用
一般用oc版的,swift也能用
*********************地图篇*************************
一.地图的基本展示
1.地图可以用一个MapView控件来展示
注意:要导入MKMapKit框架
2.地图的展示类型,可以通过属性 mapType设置
地图分为:标准地图,卫星地图,混合地图 ios9之后新出了: 三维混合/三维卫星地图
3.地图上可以展示哪些的内容
比例尺,指南针,交通状况,标志建筑,显示用户位置(后面详细介绍)
4.可以对地图进行哪些操作
缩放:zoomEnabled
旋转:rotateEnabled
滚动:scrollEnabled
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132109582-786685968.png)
二.显示用户的位置
1.怎么显示用户的位置?
1.1 设置地图的一个属性即可mapView.showsUserLocation = true 或 mapView.userTrackingMode = .FollowWithHeading/. Follow
1.2 注意:一定要设置请求授权
1.21 创建 CLLocationManager对象
1.22 调用方法授权 requestWhenInUseAuthorization 或 always
1.23 在info文件中添加对应的key值
2.获取用户的位置
2.1 设置地图的代理
2.2 实现代理方法
在代理方法中通过userLocation.location?.coordinate 拿到经纬度
3.跟踪用户的位置
3.1 首先要获取用户的位置
3.2 设置属性即可
mapView.userTrackingMode = .FollowWithHeading/. Follow
4.设置地图的显示区域
4.1 通过一个属性就可以设置(一般在代理方法中设置)
mapView.setRegion(<#T##region: MKCoordinateRegion##MKCoordinateRegion#>, animated: <#T##Bool#>)
4.2 需要传入MKCoordinateRegion参数,那么就需要创建这个参数
MKCoordinateRegion(center: <#T##CLLocationCoordinate2D#>, span: <#T##MKCoordinateSpan#>)
4.3 创建MKCoordinateRegion又需要传入CLLocationCoordinate2D和MKCoordinateSpan参数
4.4 创建CLLocationCoordinate2D参数(经纬度) 可以在代理方法中获得
4.5 创建MKCoordinateSpan参数 1纬度 = 111km
let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132223332-457180345.png)
5.点击按钮,回到用户的位置
5.1 当用户拖动地图时,想让地图回到自己的位置,如果还需要拖动回来就太麻烦了
可以设置一个按钮:点击按钮,立刻让地图的中心点就是自己的位置
5.2 怎么实现?
获取用户的位置(经纬度coordinate) ,将这个位置设置为地图的中心点即可
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132248207-650111777.png)
三.在地图上展示大头针
1.添加大头针
1.1 创建大头针对象 addAnnotation(annotation: MKAnnotation)
1.11 需要创建一个MKAnnotation
1.12 进去头文件,发现MKAnnotation 是一个协议, 也就是需要传一个遵守该协议的对象
1.13 自定义一个模型,遵守协议 , 协议里面只有三个计算属性
也就是说,只需要实现这三个属性(在模型中定义这三个属性 注意:要定义为普通属性)
1.14 创建模型对象
1.2 将大头针对象添加到mapView中
addAnnotation(annotation: MKAnnotation)
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132308441-2126885219.png)
2.点击屏幕,在点击位置添加大头针
2.1 获取用户点击的位置
2.2 将点击的点转成经纬度
2.3 根据经纬度创建大头针模型
2.4 将大头针模型添加到mapView中
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132412144-2091838952.png)
3.自定义大头针(修改大头针的子类)
3.1 系统给定的大头针样式单一,我们想要其它样式的大头针,需要自定义大头针
3.2 怎么自定义大头针?
大头针能添加到view上,一定是一个控件,只要拿到这个控件,就可以进行修改
3.3 怎么拿到大头针的view?
在代理方法中会把大头针添加到mapView上,这个时候就可以拿到
3.4 修改完大头针,发现点击大头针看不到title和subTitle了 为什么?
需要设置一个属性才可以看到annoView?.canShowCallout = true
3.5 设置大头针的样式,发现标记用户位置的图标也变为了大头针, 不想让标记位置的图标变为大头针,怎么办?
判断大头针是否为用户值得大头针 MKUserLocation是的话就返回nil (返回nil就是系统默认的大头针样式)
3.6 对大头针进行性能优化(重用)
和设置tableView的重用步骤差不多
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132434691-6635470.png)
4.自定义大头针(修改大头针,自定义大头针的image)
4.1 系统自带的大头针只能显示一个大头针,我们想让大头针显示图片,只能自定义
新建一个类,继承自 MKAnnotationView 用的时候,直接创建这个类即可
4.2 怎么设置大头针显示的图片?
只需要设置Image属性即可
4.3 如何让大头针显示不同的图片?
对大头针类型进行判断 ,给不同类型大头针设置不同的图片即可
4.4 大头针非常多,判断的话很麻烦,也没技术含量,怎么解决?
给大头针对象添加一个属性: iconName 属性里面保存对应的照片名称即可
设置的时候,只需要取出属性的值,设置给UIImageView即可
4.5 想在title左右两边也显示图片,怎么办?
设置两个属性即可 leftCalloutAccessoryView rightCalloutAccessoryView
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132613504-886199976.png)
5.代码重构
5.1 为什么要进行代码重构?
把自定义大头针的操作全部写在控制器中,控制器太臃肿
5.2 怎么对控制器进行”瘦身"
把自定义大头针的代码抽取到一个view中
5.3 怎么抽取?
自定义大头针的view ,把相关代码封装到view里面
5.4 抽取代码要用到模型和mapView怎么办?
在自定义view中定义模型属性 把mapView当成参数传进去
5.5 注意: 父类中已经存在这个模型属性了,在子类中不允许重复定义,怎么办?
重复定义属性的时候,重写属性监听器方法即可
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132702566-1145748854.png)
6.给大头针添加动画
6.1 系统自带的大头针可以设置坠落动画,自定义的大头针怎么设置动画?
我们只要拿到大头针view的frame就能实现坠落动画
6.2 怎么拿到大头针的frame
只需要获取大头针的view即可
6.3 在代理方法中可以拿到view
6.4 执行动画步骤
6.41 保存大头针的y值
6.42 设置大头针的y值为0
6.43 再设置大头针的y值为原来的值,并执行动画
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132722598-1476744151.png)
四.实现导航功能(了解)
1.利用系统的地图实现导航
1.1 在自己app中打开系统地图,实现导航
openMapsWithItems(mapItems: [MKMapItem], launchOptions: [String : AnyObject]?) -> Bool
1.2 需要在方法中传入一个数组[MKMapItem](起点,终点) 和一个字典 [String : AnyObject] (导航的参数:驾车还是步行等)
1.3 创建MKMapItem类型的起点和终点
1.4 起点通过的一个方法就能实现,终点要利用地理编码获得
1 @IBAction func startNavigating() { 2 // 1.获取用户输入的地址 3 guard let address = destinationTextField.text else { 4 return 5 } 6 // 2.地理编码 7 geocoder.geocodeAddressString(address) { (placemarks : [CLPlacemark]?, error : NSError?) in 8 // 3.对错误进行校验 9 if error != nil { 10 return 11 } 12 // 4.获取placemark 13 guard let clpm = placemarks?.first else { 14 return 15 } 16 // 5.创建终点的item 17 let mkpl = MKPlacemark(placemark: clpm) 18 let destinationItem = MKMapItem(placemark: mkpl) 19 20 // 6.获取起点的item 21 let sourceItem = MKMapItem.mapItemForCurrentLocation() 22 23 // 7.调用对应的导航方法 24 self.startNavigationWithSoureItem(sourceItem, destionationItem: destinationItem) 25 } 26 } 27 28 private func startNavigationWithSoureItem(soureItem : MKMapItem, destionationItem : MKMapItem) { 29 // 1.获取起点和终点的item,并且放入到数组中 30 let items = [soureItem, destionationItem] 31 // 2.设置导航的参数 32 /* 33 MKLaunchOptionsDirectionsModeKey: 步行/驾车 34 MKLaunchOptionsMapTypeKey: 地图类型 35 MKLaunchOptionsShowsTrafficKey: 是否显示交通状况 36 */ 37 var launchOptions = [String : NSObject]() 38 launchOptions[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving 39 launchOptions[MKLaunchOptionsMapTypeKey] = MKMapType.Hybrid.rawValue 40 launchOptions[MKLaunchOptionsShowsTrafficKey] = true 41 42 // 3.开始导航 43 MKMapItem.openMapsWithItems(items, launchOptions: launchOptions) 44 } 45 }
2.请求整个导航线路,在自己app中把线路画出来
2.1 可以通过 MKDirections对象的一个方法实现
2.2 首先要创建 MKDirections对象
MKDirections(request: <#T##MKDirectionsRequest#>)
2.3 还需要创建MKDirectionsRequest对象
2.4 创建MKDirectionsRequest对象,并设置属性(起点位置和终点位置)
2.5 通过MKDirections对象方法请求 calculateDirectionsWithCompletionHandler路线
2.6 获取所有路线,并把路线通过 addOverlay方法添加到mapView上
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132831894-1427620067.png)
五.集成百度地图
1.如何使用第三方SDK
1.1 搜索想要集成的SDK
1.2 进入官方下载SDK开发包
1.3 查看demo程序(运行看看有哪些功能)
1.4 根据API一步步集成(官方一般有文档教程)
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132900035-132494215.png)
2.基本集成
2.1 先将需要集成的功能的框架导入到项目
2.2 创建桥接文件(根据需求创建, 只有开发包是oc 自己代码是swift才需要创建)
2.3 在桥接文件中导入头文件(官方文档一般会给需要导入的头文件)
2.4 配置桥接文件
3.请求授权
3.1 如何授权?
在AppDelegate文件中进行授权
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132916019-458002199.png)
3.2 授权完,运行直接报错,为什么?
百度一些框架依赖系统的一些框架,还需要导入系统的一些框架
4. BMKMapView的展示
4.1 创建 BMKMapView对象
4.2 设置frame,添加到屏幕上
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908132937285-740757936.png)
5.定位功能的实现
通过代理就可以实现
![](https://images2015.cnblogs.com/blog/1001470/201609/1001470-20160908133040598-249877206.png)
6.POI检索功能(查找功能)
注意:发起检索一定要在地图添加到view上之后