iOS地图MapKit和定位CoreLocation(上) -- swift

老早就想总结一篇地图定位相关的文章,今天终于有空,有出错的地方欢迎指正。
一.CoreLocation的三大功能:

1.地理定位:定位用户所在的位置, 获取对应的经纬度或者海拔等信息
2.地理编码:分地理方法和反地理编码
地理编码: 北京市 北京市区------》39.9,116. 3
反地理编码: 39.9,116. 3------ 》北京市 北京市区
3.区域监听:事先在APP内部通过代码,指定一个区域, 那么当用户进入或离开区域的时候, 我们都可以监听到
定位用户所在的位置, 获取对应的经纬度或者海拔等信息

二.地理定位
<一>定位授权
iOS8.0之后,想要获取用户的位置信息,需要主动请求授权(前台定位授权or前后台定位授权)

申请前台定位授权:###

1.必须在info.plist文件中配置对应的key NSLocationWhenInUseUsageDescription
2.默认情况下只能在前台获取用户的位置信息(用户进入软件才能获取位置信息)
3.如果想要在后台也能获取用户的位置信息,需要:开启后台模式 location updates
4.在后台获取用户的位置信息,系统会在顶部显示蓝色横幅,时时提示用户该app在获取你的位置信息,点击蓝色横幅则可以打开该app(这个是不好的,小心用户卸了你的APP)
locationM.requestWhenInUseAuthorization()// 前台定位授权

申请前后台定位授权:###

1.不会出现蓝色横幅
2.必须在info.plist文件中配置对应的key NSLocationAlwaysUsageDescription
3.在前台和后台都能够获取用户的位置信息,在后台获取用户的位置信息,不需要开启后台模式
locationM.requestAlwaysAuthorization()// 前后台定位授权

iOS9.0之后

申请前台定位授权:

(跟iOS8.0一样,只是在前台授权中,想在后台获取用户位置时多了如下条件)
1.需要开启后台模式: location updates
2.必须允许后台获取用户的位置信息(下面的版本适配代码)
3.在后台获取用户的位置信息,也会出现iOS8.0的那个蓝色横幅提示信息
注意点: 如果允许后台获取用户的位置信息,必须勾选后台模式,否则此代码会造成崩溃

if #available(iOS 9.0, *) {
locationM.allowsBackgroundLocationUpdates = true
 }

申请前后台定位授权:相对于iOS8.0,没有什么更新的东西
注意:如果是做监听区域,使用的授权必须是前后台定位授权
<二>.定位是使用CoreLocation这个框架实现的,而跟定位离不开关系的是位置管理者 CLLocationManager,使用位置管理者的时候,通常会提供一个懒加载的方法来保证他不被销毁.苹果给他提供了代理的接口,外界可以通过设置代理来获取他所能管理的所有东西和动态.下面介绍一些地理定位时常用的代理方法:

/// 代理方法一:当获取到用户的位置的时候会来到该方法
/// - Parameters:
///   - manager: 位置管理者
///   - locations: 位置数组
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//locations是一个保存着CLLocation对象的集合,我们一般都是取出last,因为他是最新的.
       print("定位到了")
}

/// 代理方法二:当授权状态发生改变的时候会来到该方法(授权状态不受开发人员控制,由用户控制)

/// - Parameters:
///   - manager: 位置管理者
///   - status: 授权状态( notDetermined, denied, restricted, authorizedAlways, authorizedWhenInUse)

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {}

///代理方法三:定位失败的时候会来到该方法

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("定位失败")
}

注意:当用户自己拒绝APP对他进行位置定位的时或手机主人之前已经把手机的定位功能关闭时,status都是拒绝状态,如果手机的定位功能处于关闭状态,苹果会自动跳转到手机设置界面,但是如果是用户自己在打开APP时直接选择拒绝的,苹果不会自动跳转手机设置界面,需要开发者做友情提示用户自己去开启.
<三>.给位置管理者CLLocationManager设置一些条件来进行定位(以下出现locationM的地方都是代表CLLocationManager的一个实例对象)

1.locationM.distanceFilter = 100//相对于上一次位置,大于100米就获取一次用户的位置信息
2. locationM.desiredAccuracy = kCLLocationAccuracyBest//想要的精确度
//AccuracyBestForNavigation : 最适合导航
//AccuracyBest : 最好的
//AccuracyNearestTenMeters : 附近10米
//AccuracyHundredMeters : 附近100米
//AccuracyKilometer : 附近1000米
//AccuracyThreeKilometers : 附近3000米
注意:精确度越高,越耗电,定位时间越久

<四>.启动监听的方法

1.locationM.startUpdatingLocation()
//启动监听用户位置的方法,该方法会不断获取用户位置信息,如果只想获取一次位置信息,可以在代理方法一中停止监听   manager.stopUpdatingLocation()
// startUpdatingLocation()方法被称为标准定位服务(基于GGPS/wifi/蓝牙/蜂窝基站)
// GPS:定位最准确,缺点:如果被建筑物挡住,就检测不到了
// wifi:不能使用则GPS使用wifi
// 蜂窝基站: 不能使用wifi,则使用蜂窝基站
// 蓝牙 : iwatch蓝牙连接手机
// 优点: 定位精确度高
// 缺点: 比较耗电,APP必须运行着才能获取用户的位置信息
// 具体使用什么方式进行定位由苹果决定
2.locationM.startMonitoringSignificantLocationChanges()
// 监听重大位置改变(基于基站定位,意思是要有电话卡才能监听)
// 优点: 比较省电,z只要APP还安装在用户手机上,都可以获取用户的位置信息,当用户的位置发生改变的时候,会自动启动该app在后台执行,来获取用户的位置信息
// 缺点: 定位精确度低,硬性要求必须有电话模块

<五>.CLLocation的详细解释 (经常在代理方法中返回的对象)
CLLocation : 位置对象,他里面可以拿到的信息有很多,下面只是了解其中的小部分

// coordinate : 经纬度信息
// altitude : 海拔
// horizontalAccuracy : 水平精确度,如果为负数,代表位置不可用,
// verticalAccuracy : 垂直精确度,如果为负数,代表海拔不可用
// course : 航向,如果为负数,代表航向不可用
// speed : 速度,如果为负数,代表速度不可用
// timestamp : 获取当前定位到的时间
// distance(from location: CLLocation) : 计算2个位置之间的实际物理距离
//打印这个对象获得的信息: <+38.78583400(纬度),-122.40641700(经度)> +/- 5.00m(水平精确度) (speed(速度) -1.00 mps / course(航向) -1.00) @ 11/11/16, 2:48:45 PM China Standard Time(获取当前位置的时间)
// 可以返回负数的那些参数,利用他们筛选数据,判断哪些信息需要上传后台

三. 地理编码

 @IBOut let weak var addressTV: UITextView!
 @IBOut let weak var latitudeTF: UITextField!
 @IBOut let weak var longitudeTF: UITextField!
   /// 必须联网!!!
lazy var geoc : CLGeocoder = {
 return CLGeocoder()//CLGeocoder地理编码要使用的类
 }()
//监听地理编码的点击
@IBAction func geocClick() {
let address = addressTV.text!
if address.characters.count == 0 {return}
/// 地理编码的方法,闭包参考参数public typealias CLGeocodeCompletionHandler = ([CLPlacemark]?, Error?) -> Swift.Void()    swift3.0后闭包敲回车不管用了,这个方法可以知道参数是什么
geoc.geocodeAddressString(address, completionHandler: {(clpls : [CLPlacemark]?,error : Error? )in
[CLPlacemark] : 地标对象的数组,
//地标对象可以拿到的信息: location : 用户的位置(经纬度信息)// name : 地址// locality : 城市// country : 国家
if error != nil {
return
}
// 1.获取地标对象(第一个相关度是最高的)
guard let clpl = clpls?.first else {return}
// 2.设置地址
self.addressTV.text = "国家:\\(clpl.country ?? "")\\n 城市:\\(clpl.locality ?? "")\\n 地址:\\(clpl.name ?? "")"
// 3.设置latitudeTF该显示的经纬度
self.latitudeTF.text = clpl.location?.coordinate.latitude.description ?? ""
self.longitudeTF.text = clpl.location?.coordinate.longitude.description ?? ""
})
}
//监听反地理编码方法的点击
@IBAction func reverseGeocClick() {
let latitude = CLLocationDegrees(latitudeTF.text!) ?? 0
let longitude = CLLocationDegrees(longitudeTF.text!) ?? 0
let location = CLLocation(latitude: latitude, longitude: longitude)
/// 反地理编码的方法
geoc.reverseGeocodeLocation(location, completionHandler: {(clpls : [CLPlacemark]?,error : Error? )in
if error != nil {return}
// 1.获取地标对象(第一个相关度是最高的)
guard let clpl = clpls?.first else {return}
// 2.设置地址
self.addressTV.text = "国家:\\(clpl.country ?? "")\\n 城市:\\(clpl.locality ?? "")\\n 地址:\\(clpl.name ?? "")"
// 3.设置经纬度
self.latitudeTF.text = clpl.location?.coordinate.latitude.description ?? ""
self.longitudeTF.text = clpl.location?.coordinate.longitude.description ?? ""
})
}

四.区域监听

<一>开启区域监听

override func viewDidLoad() {
       super.viewDidLoad()
//设置监听区域后开启监听
// center : 经纬度(区域中心点)
// radius : 区域的半径
// identifier : 区域的标识
let center : CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 12.123, longitude: 123.456)
let radius : CLLocationDistance = 1000
let region = CLCircularRegion(center: center, radius: radius, identifier: "广州")
locationM.startMonitoring(for: region)
// 主动请求用户的位置对于这个区域的状态,用来提醒用户自己目前的状态
// 一个区域被监听之后,才能去请求这个区域的状态
// 当一个区域能被监听之后(didStartMonitoringFor),会把该区域放入集合:monitoredRegions
// 这个集合最多能存储20个区域,集合不能重复
if locationM.monitoredRegions.first != nil {//判断集合中有没有保存过 监听区域
locationM.requestState(for: region)
       }
}

<二>区域监听的相关代理方法讲解

/// 当这个区域开始被监听的时候,就会来到didStartMonitoringFor方法

/// 能够来到这里,说明这个区域是可以被监听的
/// - Parameters:
///   - manager: 位置管理者
///   - region: 区域
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
       print("开始监听这个区域 -> \\(region.identifier)")
   }

/// 进入该区域的时候会来到didEnterRegion方法(动作)

/// - Parameters:
///   - manager: 位置管理者
///   - region: 区域
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("进入该区域 -> \\(region.identifier)")
}

/// 离开该区域的时候会来到didExitRegion方法(动作)

/// - Parameters:
///   - manager: 位置管理者
///   - region: 区域
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("离开该区域 -> \\(region.identifier)")
}

/// 当这个区域不能够被监听时候,就会来到monitoringDidFailFor region方法

/// 为什么不能被监听?: 监听的区域超过20个/写的区域半径太大,经纬度信息不对
/// - Parameters:
///   - manager: 位置管理者
///   - region: 区域
///   - error: 错误信息
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
// 如果添加满了20个,则会来到该代理方法中
// 在这里一般移除与用户当前位置最远的那个区域
// 移除掉之后就可以添加新的了
print("此区域无法被监听 -> \\(region?.identifier)")
}

/// 当请求区域状态的时候,会来到didDetermineState方法

/// 如果用户对于这个区域的状态发生改变也会来到该方法(动作)
/// - Parameters:
///   - manager: 位置管理者
///   - state: 状态
///   - region: 区域
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
switch state {
case .inside:
print("进入区域状态 -> \\(region.identifier)")
case .outside:
print("离开区域状态 -> \\(region.identifier)")
case .unknown:
print("没有获取用户的位置")
   }
}

the end, it's hard to write an article, hope this can help you a little bit. Thanks for reading.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容