iOS 地图

iOS 地图 基础 一 :定位

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

简介

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

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

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

定位 (CoreLocation)

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

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

  • CLLocationManager

@property(assign, nonatomic, nullable) id 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 
    @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

iOS 地图_第1张图片
CLLocationCoordinate2D 简介.png

关于经纬度的知识

iOS 地图_第2张图片
经纬度简介.png

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


iOS 地图_第3张图片
自定义模拟器坐标方法.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 
    
        @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 
#import  // 1.加载头文件
#import 
#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的描述


iOS 地图_第4张图片
iOS8 之后 定位需要添加的授权定位字段 例子.png

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

  • 如果当前的授权状态是 前台定位授权,那么你需要勾选后台模式location updates
    iOS 地图_第5张图片
    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

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

你可能感兴趣的:(iOS 地图)