iOS 地圖

iOS 地圖 基礎(chǔ) 一 :定位

在 iOS 程序開發(fā)中 地圖功能是普遍存在的,剛好最近都在研究地圖的功能。總結(jié)一下,“好記性不如爛筆頭”。

簡介

地圖應(yīng)用主要分為 定位地圖 功能。分別對應(yīng) CoreLocation 與 MapKit 框架。

  • CoreLocation核心定位內(nèi)容: 【著重功能實現(xiàn)】
    1.地理定位(用戶個人位置信息等)
    2.地理編碼(地理編碼與反地理編碼)
    3.區(qū)域監(jiān)聽(比如一個區(qū)域 監(jiān)聽是否進(jìn)入或者已經(jīng)退出 該區(qū)域)
  • MapKit:【著重界面展示】
    用于地圖展示,例如大頭針,導(dǎo)航路線、覆蓋層展示等。

下面從簡單開始,我們首先了解一下怎么使用CoreLocation進(jìn)行地圖定位的。

定位 (CoreLocation)

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

CoreLocation框架中所有類的前綴都是CL
CoreLocation中使用 CLLocationManager 對象來做用戶定位管理

  • CLLocationManager

@property(assign, nonatomic, nullable) id<CLLocationManagerDelegate> delegate;
// 指定最小更新距離(以米為單位)。設(shè)置當(dāng)用戶超過指定的最小更新距離才會返回通知。默認(rèn)為None,及時更新.
@property(assign, nonatomic) CLLocationDistance distanceFilter;

// 期待的精準(zhǔn)度 (可以根據(jù)不用的使用場景進(jìn)行適合的精度值選擇,越精準(zhǔn),耗電大)
 kCLLocationAccuracyBest
  *達(dá)到最佳的準(zhǔn)確度。
 kCLLocationAccuracyBestForNavigation
  *用于導(dǎo)航。
  ...
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;

// 導(dǎo)航方向期待精準(zhǔn)度 默認(rèn)為1度就會 回調(diào)用戶信息
@property(assign, nonatomic) CLLocationDegrees headingFilter __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;

// 其他更多參數(shù)解析 參考于: https://m.aliyun.com/yunqi/articles/39192 

CLLocationManager 的常用操作
1.開始更新用戶位置

 - (void)startUpdatingLocation;

  [self.locationManager startUpdatingLocation];//開啟定位

2.停止更新用戶位置

 -(void)stopUpdatingLocation;

  [self.locationManager stopUpdatingLocation];//開啟定位

當(dāng)調(diào)用startUpdatingLocation 方法后,就開始請求、刷新用戶的位置,當(dāng)前用戶位置與上一次位置超過規(guī)定精準(zhǔn)度距離或者開啟定位,就會調(diào)用以下代理方法:

        /**
           *  獲取到用戶位置之后調(diào)用
           *
           *  @param manager   位置管理者
           *  @param locations 位置信息數(shù)組
           */
          -(void)locationManager:(CLLocationManager   *)manager
              didUpdateLocations:(NSArray *)locations;
  • locations里面包含了CLLocation定位信息模型。

3.為了嚴(yán)謹(jǐn)起見,最好在使用定位功能之前判斷當(dāng)前應(yīng)用定位是否可用。判斷當(dāng)前應(yīng)用的定位功能是否可以用:
+(BOOL)locationServicesEnabled;

  • CLLocation 【定位信息模型】

CLLocation 用來表示某個位置的地理信息,如經(jīng)緯度、海拔、航向角度、移動速度等。

    @interface CLLocation : NSObject <NSCopying, NSSecureCoding>
    @property(readonly, nonatomic) CLLocationCoordinate2D coordinate;   //經(jīng)緯度
    @property(readonly, nonatomic) CLLocationDistance altitude;  //海拔
    @property(readonly, nonatomic) CLLocationDirection course; //路線,航向(取值范圍是0.0° ~ 359.9°,0.0°代表真北方向)
    @property(readonly, nonatomic) CLLocationSpeed speed; //移動速度(單位是m/s)
    ... 具體內(nèi)容... 用到再分析

CLLocation 對象方法 可以計算2個位置之間的距離

    -(CLLocationDistance)distanceFromLocation:(constCLLocation*)location;
  • 定位權(quán)限問題

!要想使用LBS定位,必須先向用戶主動申請授權(quán)。
從iOS6 開始蘋果越來越注重用戶的隱私問題,而在定位上 iOS8 、iOS9 都做了相應(yīng)的規(guī)定。比如iOS8開始需要開發(fā)者主動向用戶申請授權(quán),iOS9 后臺定位的設(shè)置等。具體解決方法,后面例子中會說明操作。

  • CLLocationCoordinate2D

CLLocationCoordinate2D 簡介.png

關(guān)于經(jīng)緯度的知識

經(jīng)緯度簡介.png

使用模擬器自定義定位坐標(biāo)進(jìn)行測試,具體操作為:


自定義模擬器坐標(biāo)方法.png
  • CLGeocoder (2.地理編碼 [地理編碼與反地理編碼])

  • 地理編碼:根據(jù)給定的地名,獲得具體的位置信息。(比如經(jīng)緯度、地址的全稱等)
    CLGeocoder 對象方法:

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

反地理編碼方法: 根據(jù)給定的經(jīng)緯度,獲得具體的位置信息。

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

當(dāng)進(jìn)行地理編碼、反地理編碼調(diào)用成功之后,調(diào)用(CLGeocodeCompletionHandler)completionHandler ,其結(jié)構(gòu)為:

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

error: 返回錯誤信息
Placemarks:含有CLPlacemark 對象的數(shù)組

  • CLPlacemark 存儲地標(biāo)信息模型

        @interface CLPlacemark : NSObject <NSCopying, NSSecureCoding>
    
        @property (nonatomic, readonly) CLLocation *location;  //地理位置
        @property (nonatomic, readonly) CLRegion *region; //區(qū)域
        @property (nonatomic, readonly) NSDictionary *addressDictionary; //詳細(xì)的地址信息
        @property (nonatomic, readonly) NSString *name; //地址名稱
        @property (nonatomic, readonly) NSString *locality; //城市
        ...
    

3.(監(jiān)聽區(qū)域)

  • 創(chuàng)建監(jiān)聽區(qū)域、請求區(qū)域

創(chuàng)建監(jiān)聽區(qū)域主要設(shè)置 (區(qū)域中心點與區(qū)域半徑)

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1. 判斷區(qū)域監(jiān)聽服務(wù)是否可用(定位服務(wù)是否關(guān)閉, 定位是否授權(quán), 是否開啟飛行模式)
    if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {

        // 創(chuàng)建區(qū)域中心
        CLLocationCoordinate2D center = CLLocationCoordinate2DMake(29.11111, 131.2222);
        // 創(chuàng)建區(qū)域(指定區(qū)域中心,和區(qū)域半徑)
        CLLocationDistance radius = 1000;

        // 判斷區(qū)域半徑是否大于最大監(jiān)聽區(qū)域半徑,如果大于, 就沒法監(jiān)聽。(在前年創(chuàng)建定位的時候,也有設(shè)置判斷定位區(qū)域大小)
        if (radius > self.locationM.maximumRegionMonitoringDistance) {
            radius = self.locationM.maximumRegionMonitoringDistance;
        }
        CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"區(qū)域1"];

        // 開始監(jiān)聽指定區(qū)域
        [self.locationM startMonitoringForRegion:region];
        // 請求某個區(qū)域的狀態(tài)
        [self.locationM requestStateForRegion:region];
    }else
    {
        NSLog(@"區(qū)域監(jiān)聽不可用");
    }

}

#pragma mark - CLLocationManagerDelegate

// 進(jìn)去監(jiān)聽區(qū)域后調(diào)用(調(diào)用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didEnterRegion:(nonnull CLRegion *)region
{
    NSLog(@"進(jìn)入?yún)^(qū)域---%@", region.identifier);
    [manager stopMonitoringForRegion:region];
}

// 離開監(jiān)聽區(qū)域后調(diào)用(調(diào)用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didExitRegion:(nonnull CLRegion *)region
{
    NSLog(@"離開區(qū)域---%@", region.identifier);
}

// 當(dāng)監(jiān)聽區(qū)域失敗時調(diào)用(監(jiān)聽區(qū)域個數(shù)是有上限的, 如果大于上限,再創(chuàng)建區(qū)域就會失敗,就會執(zhí)行此方法)
-(void)locationManager:(nonnull CLLocationManager *)manager monitoringDidFailForRegion:(nullable CLRegion *)region withError:(nonnull NSError *)error
{
 //  一般都是在此處把比較遠(yuǎn)的區(qū)域給移除
 //  [manager stopMonitoringForRegion:];

}

// 請求某個區(qū)域狀態(tài)時, 回調(diào)的代理方法
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    switch (state) {
        case CLRegionStateUnknown:
            NSLog(@"未知狀態(tài)");
            break;
        case CLRegionStateInside:
            NSLog(@"在區(qū)域內(nèi)部");
            break;
        case CLRegionStateOutside:
            NSLog(@"在區(qū)域外部");
            break;
        default:
            break;
    }
}

=======

下面進(jìn)行一個簡單的封裝 個人定位 與 獲取地理位置信息 的工具類進(jìn)行分析。
代碼:

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); // 單例,(由于在項目中,可能存在不同地方 多次調(diào)用個人定位,所以設(shè)置為單例)

/**
 獲取當(dāng)前位置

 @param block 獲取當(dāng)前位置后處理的block
 */
- (void)getCurrentLocation:(ResultBlock)block;

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

@end

授權(quán)問題

1、在iOS7之前,當(dāng)用到個人定位的時候,系統(tǒng)會自動提示用戶是否允許定位授權(quán)。
2、而在iOS8之后,蘋果要求開發(fā)者主動調(diào)用提示用戶允許定位授權(quán),而且必須在info.plist 文件中配置一項屬性才能彈出授權(quán)窗口,分別為
NSLocationWhenInUseDescription ,允許在前臺獲取GPS的描述
NSLocationAlwaysUsageDescription ,允許在前后臺獲取GPS的描述


iOS8 之后 定位需要添加的授權(quán)定位字段 例子.png

3、如果在iOS9以后,想要在后臺獲取用戶位置:

  • 如果當(dāng)前的授權(quán)狀態(tài)是 前臺定位授權(quán),那么你需要勾選后臺模式location updates
    iOS 9 前臺模式授權(quán) 設(shè)置后臺定位方法.png
 NSArray *backModes = [infoDict valueForKey:@"UIBackgroundModes"]; // 獲取后臺參數(shù)數(shù)組
        if ([backModes containsObject:@"location"]) {
          self.locaManager.allowsBackgroundLocationUpdates = YES; (設(shè)置為YES后 當(dāng)使用When 授權(quán)時,運行在后臺不會出現(xiàn)藍(lán)條)      // 判斷后臺模式中是否包含位置更新服務(wù)
        }
  • 如果當(dāng)前的授權(quán)狀態(tài)是 前后臺定位授權(quán),那么默認(rèn)情況下,就可以在后臺獲取用戶位置信息,不需要勾選后臺模式 (location updates)

SLLcoationTool 簡單的進(jìn)行獲取個人定位的位置與信息 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) { //定位獲取授權(quán)成功
//            NSLog(@"%f : %f",currentLoc.coordinate.latitude,currentLoc.coordinate.longitude);
            if (placemark.name)  NSLog(@"%@",placemark.name);
        }else { // 定位獲取授權(quán)失敗
            //情景一:一旦用戶選擇了“Don’t Allow”,意味著你的應(yīng)用以后就無法使用定位功能,且當(dāng)用戶第一次選擇了之后,以后就再也不會提醒進(jìn)行設(shè)置。所以當(dāng)?shù)谝淮伪痪芙^事,可以通過代碼跳轉(zhuǎn)到設(shè)置界面。
                UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:error message:@"用戶拒接定位,請在設(shè)置-私隱-中允許app授權(quán)定位" preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                    NSURL *url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
                    if ([[UIApplication sharedApplication] canOpenURL:url]) { //打開設(shè)置的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];
            }
    }];

!! 要注意的是, 獲取個人定位信息是異步的,如果在程序中的邏輯需要 使用到 個人定位信息,要確保在 使用前 個人定位信息 是否存在。

具體代碼已經(jīng) post 上 Github:https://github.com/NeverLand-LLT/CoreLocationDemo

感謝你們的閱讀,歡迎來糾錯與交流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容