美文网首页Swift
iOS地图MapKit和定位CoreLocation(上) --

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

作者: Junin | 来源:发表于2016-12-03 21:36 被阅读943次

    老早就想总结一篇地图定位相关的文章,今天终于有空,有出错的地方欢迎指正。
    一.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.

    相关文章

      网友评论

        本文标题:iOS地图MapKit和定位CoreLocation(上) --

        本文链接:https://www.haomeiwen.com/subject/ysicmttx.html