iOS 地图

iOS 地图 基础 一 :定位

在 iOS 程序开发中 地图功能是普遍存在的,刚好最近都在研究地图的功能。总结一下,“好记性不如烂笔头”。

简介

地图应用主要分为 定位地图 功能。分别对应 CoreLocation 与 MapKit 框架。

  • CoreLocation核心定位内容: 【着重功能实现】
    1.地理定位(用户个人位置信息等)
    2.地理编码(地理编码与反地理编码)
    3.区域监听(比如一个区域 监听是否进入或者已经退出 该区域)
  • MapKit:【着重界面展示】
    用于地图展示,例如大头针,导航路线、覆盖层展示等。

下面从简单开始,我们首先了解一下怎么使用CoreLocation进行地图定位的。

定位 (CoreLocation)

CoreLocation 框架的使用(1.地理定位)

CoreLocation框架中所有类的前缀都是CL
CoreLocation中使用 CLLocationManager 对象来做用户定位管理

  • CLLocationManager

@property(assign, nonatomic, nullable) id<CLLocationManagerDelegate> delegate;
// 指定最小更新距离(以米为单位)。设置当用户超过指定的最小更新距离才会返回通知。默认为None,及时更新.
@property(assign, nonatomic) CLLocationDistance distanceFilter;

// 期待的精准度 (可以根据不用的使用场景进行适合的精度值选择,越精准,耗电大)
 kCLLocationAccuracyBest
  *达到最佳的准确度。
 kCLLocationAccuracyBestForNavigation
  *用于导航。
  ...
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;

// 导航方向期待精准度 默认为1度就会 回调用户信息
@property(assign, nonatomic) CLLocationDegrees headingFilter __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;

// 其他更多参数解析 参考于: https://m.aliyun.com/yunqi/articles/39192 

CLLocationManager 的常用操作
1.开始更新用户位置

 - (void)startUpdatingLocation;

  [self.locationManager startUpdatingLocation];//开启定位

2.停止更新用户位置

 -(void)stopUpdatingLocation;

  [self.locationManager stopUpdatingLocation];//开启定位

当调用startUpdatingLocation 方法后,就开始请求、刷新用户的位置,当前用户位置与上一次位置超过规定精准度距离或者开启定位,就会调用以下代理方法:

        /**
           *  获取到用户位置之后调用
           *
           *  @param manager   位置管理者
           *  @param locations 位置信息数组
           */
          -(void)locationManager:(CLLocationManager   *)manager
              didUpdateLocations:(NSArray *)locations;
  • locations里面包含了CLLocation定位信息模型。

3.为了严谨起见,最好在使用定位功能之前判断当前应用定位是否可用。判断当前应用的定位功能是否可以用:
+(BOOL)locationServicesEnabled;

  • CLLocation 【定位信息模型】

CLLocation 用来表示某个位置的地理信息,如经纬度、海拔、航向角度、移动速度等。

    @interface CLLocation : NSObject <NSCopying, NSSecureCoding>
    @property(readonly, nonatomic) CLLocationCoordinate2D coordinate;   //经纬度
    @property(readonly, nonatomic) CLLocationDistance altitude;  //海拔
    @property(readonly, nonatomic) CLLocationDirection course; //路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表真北方向)
    @property(readonly, nonatomic) CLLocationSpeed speed; //移动速度(单位是m/s)
    ... 具体内容... 用到再分析

CLLocation 对象方法 可以计算2个位置之间的距离

    -(CLLocationDistance)distanceFromLocation:(constCLLocation*)location;
  • 定位权限问题

!要想使用LBS定位,必须先向用户主动申请授权。
从iOS6 开始苹果越来越注重用户的隐私问题,而在定位上 iOS8 、iOS9 都做了相应的规定。比如iOS8开始需要开发者主动向用户申请授权,iOS9 后台定位的设置等。具体解决方法,后面例子中会说明操作。

  • CLLocationCoordinate2D

CLLocationCoordinate2D 简介.png

关于经纬度的知识

经纬度简介.png

使用模拟器自定义定位坐标进行测试,具体操作为:


自定义模拟器坐标方法.png
  • CLGeocoder (2.地理编码 [地理编码与反地理编码])

  • 地理编码:根据给定的地名,获得具体的位置信息。(比如经纬度、地址的全称等)
    CLGeocoder 对象方法:

    - (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

反地理编码方法: 根据给定的经纬度,获得具体的位置信息。

  - (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

当进行地理编码、反地理编码调用成功之后,调用(CLGeocodeCompletionHandler)completionHandler ,其结构为:

    typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);

error: 返回错误信息
Placemarks:含有CLPlacemark 对象的数组

  • CLPlacemark 存储地标信息模型

        @interface CLPlacemark : NSObject <NSCopying, NSSecureCoding>
    
        @property (nonatomic, readonly) CLLocation *location;  //地理位置
        @property (nonatomic, readonly) CLRegion *region; //区域
        @property (nonatomic, readonly) NSDictionary *addressDictionary; //详细的地址信息
        @property (nonatomic, readonly) NSString *name; //地址名称
        @property (nonatomic, readonly) NSString *locality; //城市
        ...
    

3.(监听区域)

  • 创建监听区域、请求区域

创建监听区域主要设置 (区域中心点与区域半径)

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1. 判断区域监听服务是否可用(定位服务是否关闭, 定位是否授权, 是否开启飞行模式)
    if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {

        // 创建区域中心
        CLLocationCoordinate2D center = CLLocationCoordinate2DMake(29.11111, 131.2222);
        // 创建区域(指定区域中心,和区域半径)
        CLLocationDistance radius = 1000;

        // 判断区域半径是否大于最大监听区域半径,如果大于, 就没法监听。(在前年创建定位的时候,也有设置判断定位区域大小)
        if (radius > self.locationM.maximumRegionMonitoringDistance) {
            radius = self.locationM.maximumRegionMonitoringDistance;
        }
        CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"区域1"];

        // 开始监听指定区域
        [self.locationM startMonitoringForRegion:region];
        // 请求某个区域的状态
        [self.locationM requestStateForRegion:region];
    }else
    {
        NSLog(@"区域监听不可用");
    }

}

#pragma mark - CLLocationManagerDelegate

// 进去监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didEnterRegion:(nonnull CLRegion *)region
{
    NSLog(@"进入区域---%@", region.identifier);
    [manager stopMonitoringForRegion:region];
}

// 离开监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didExitRegion:(nonnull CLRegion *)region
{
    NSLog(@"离开区域---%@", region.identifier);
}

// 当监听区域失败时调用(监听区域个数是有上限的, 如果大于上限,再创建区域就会失败,就会执行此方法)
-(void)locationManager:(nonnull CLLocationManager *)manager monitoringDidFailForRegion:(nullable CLRegion *)region withError:(nonnull NSError *)error
{
 //  一般都是在此处把比较远的区域给移除
 //  [manager stopMonitoringForRegion:];

}

// 请求某个区域状态时, 回调的代理方法
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    switch (state) {
        case CLRegionStateUnknown:
            NSLog(@"未知状态");
            break;
        case CLRegionStateInside:
            NSLog(@"在区域内部");
            break;
        case CLRegionStateOutside:
            NSLog(@"在区域外部");
            break;
        default:
            break;
    }
}

=======

下面进行一个简单的封装 个人定位 与 获取地理位置信息 的工具类进行分析。
代码:

SLLocationTool.h

//
//  SLLocationTool.h
//  CoreLoationMapViewDemo
//
//  Created by Melody on 2016/12/2.
//  Copyright © 2016年 admin. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h> // 1.加载头文件
#import <UIKit/UIKit.h>
#import "Singleton.h" //(封装了生成单例的宏)

typedef void(^ResultBlock) (CLLocation *currentLoc,CLPlacemark *placemark,NSString *error);  

@interface SLLocationTool : NSObject

single_interface(SLLocationTool); // 单例,(由于在项目中,可能存在不同地方 多次调用个人定位,所以设置为单例)

/**
 获取当前位置

 @param block 获取当前位置后处理的block
 */
- (void)getCurrentLocation:(ResultBlock)block;

/**
 停止定位
 */
- (void)stopUpdatingLocation;

@end

授权问题

1、在iOS7之前,当用到个人定位的时候,系统会自动提示用户是否允许定位授权。
2、而在iOS8之后,苹果要求开发者主动调用提示用户允许定位授权,而且必须在info.plist 文件中配置一项属性才能弹出授权窗口,分别为
NSLocationWhenInUseDescription ,允许在前台获取GPS的描述
NSLocationAlwaysUsageDescription ,允许在前后台获取GPS的描述


iOS8 之后 定位需要添加的授权定位字段 例子.png

3、如果在iOS9以后,想要在后台获取用户位置:

  • 如果当前的授权状态是 前台定位授权,那么你需要勾选后台模式location updates
    iOS 9 前台模式授权 设置后台定位方法.png
 NSArray *backModes = [infoDict valueForKey:@"UIBackgroundModes"]; // 获取后台参数数组
        if ([backModes containsObject:@"location"]) {
          self.locaManager.allowsBackgroundLocationUpdates = YES; (设置为YES后 当使用When 授权时,运行在后台不会出现蓝条)      // 判断后台模式中是否包含位置更新服务
        }
  • 如果当前的授权状态是 前后台定位授权,那么默认情况下,就可以在后台获取用户位置信息,不需要勾选后台模式 (location updates)

SLLcoationTool 简单的进行获取个人定位的位置与信息 Demo :

    [[SLLocationTool sharedSLLocationTool] getCurrentLocation:^(CLLocation *currentLoc, CLPlacemark *placemark, NSString *error) {
        if ([error length] == 0) {
            NSLog(@"%@   ----   %@", currentLoc, placemark.name);
            NSLog(@"%f : %f",currentLoc.coordinate.latitude,currentLoc.coordinate.longitude);
            NSLog(@"%@",placemark.locality);   
        }        
    }];

或者:

[[SLLocationTool sharedSLLocationTool] getCurrentLocation:^(CLLocation *currentLoc, CLPlacemark *placemark, NSString *error) {
        if ([error length] == 0) { //定位获取授权成功
//            NSLog(@"%f : %f",currentLoc.coordinate.latitude,currentLoc.coordinate.longitude);
            if (placemark.name)  NSLog(@"%@",placemark.name);
        }else { // 定位获取授权失败
            //情景一:一旦用户选择了“Don’t Allow”,意味着你的应用以后就无法使用定位功能,且当用户第一次选择了之后,以后就再也不会提醒进行设置。所以当第一次被拒绝事,可以通过代码跳转到设置界面。
                UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:error message:@"用户拒接定位,请在设置-私隐-中允许app授权定位" preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                    NSURL *url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
                    if ([[UIApplication sharedApplication] canOpenURL:url]) { //打开设置的URL
                        if (kDeviceSysVersion < 10.0) {
                            [[UIApplication sharedApplication] openURL:url];
                        }else {
                            [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
                        }
                    }
                }];
                [alertVC addAction:cancelAction];
                [self.window.rootViewController presentViewController:alertVC animated:YES completion:nil];
            }
    }];

!! 要注意的是, 获取个人定位信息是异步的,如果在程序中的逻辑需要 使用到 个人定位信息,要确保在 使用前 个人定位信息 是否存在。

具体代码已经 post 上 Github:https://github.com/NeverLand-LLT/CoreLocationDemo

感谢你们的阅读,欢迎来纠错与交流。

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

推荐阅读更多精彩内容