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
關(guān)于經(jīng)緯度的知識
使用模擬器自定義定位坐標(biāo)進(jìn)行測試,具體操作為:
-
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的描述
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
感謝你們的閱讀,歡迎來糾錯與交流。