一. 簡介
在我們?nèi)粘I钪袝r常用到地圖和定位功能蔼紧,來導(dǎo)航去你想去的地方或者尋找周邊的景點,餐廳狠轻,電影院等等奸例,在iOS開發(fā)中,要想加入這兩大功能向楼,必須基于兩個框架進行開發(fā)查吊,有了這兩個框架,想去哪就去哪湖蜕。
CoreLocation :用于地理定位逻卖,地理編碼,區(qū)域監(jiān)聽等(著重功能實現(xiàn))
MapKit :用于地圖展示昭抒,例如大頭針评也,路線、覆蓋層展示等(著重界面展示)
二. CoreLocation框架的基本使用
1. CoreLocation使用步驟
- 導(dǎo)入CoreLocation框架灭返。
- 創(chuàng)建CLLocationManager管理者對象盗迟。
- 遵循代理,并實現(xiàn)代理方法熙含。
- 設(shè)置獲取用戶前后臺定位授權(quán)
- 開始定位罚缕。
三. CLLocationManager的使用
學(xué)習(xí)CLLocationManager可以分為三個部分。1.定位 2.手機朝向 3.區(qū)域監(jiān)聽
1. CLLocationManager -- 定位
先通過一個簡單例子看一下
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property(nonatomic,strong)CLLocationManager *locationM;
@end
@implementation ViewController
-(CLLocationManager *)locationM
{
if (_locationM == nil) {
_locationM = [[CLLocationManager alloc]init];
_locationM.delegate = self;
// 只在前臺開始定位 修改plist文件 提醒用戶
[_locationM requestWhenInUseAuthorization];
// 前后臺都可以定位 修改plist文件 提醒用戶
// [_locationM requestAlwaysAuthorization];
}
return _locationM;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 開始標(biāo)準(zhǔn)定位服務(wù)
[self.locationM startUpdatingLocation];
// 開啟顯著位置變化定位服務(wù)
// [self.locationM startMonitoringSignificantLocationChanges];
}
#pragma mark CLLocationManagerDelegate代理方法
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
// manager 位置管理者 locations 位置數(shù)組
// 在這里拿到位置信息做一些處理婆芦,這個方法會被持續(xù)調(diào)
NSLog(@"--開始定位--");
}
注意:
1. 獲取前臺定位授權(quán)[_locationM requestWhenInUseAuthorization];
需要在plist文件中加入NSLocationWhenInUseUsageDescription
怕磨。
獲取前后臺定位授權(quán)[_locationM requestAlwaysAuthorization];
需要在plist文件中加入NSLocationAlwaysUsageDescription
。
plist中加入的信息表示獲取定位授權(quán)時顯示的信息消约。
運行程序請求用戶授權(quán)時會彈出
2. 開啟標(biāo)準(zhǔn)定位服務(wù)使用的是GPS/WIFI定位肠鲫,精確度較高,關(guān)閉應(yīng)用程序就無法獲取位置或粮,而開啟顯著位置變化定位服務(wù)使用基站定位(必須有電話模塊)导饲,當(dāng)應(yīng)用程序被關(guān)閉時,也可以接受到位置通知氯材,并讓app進入后臺處理渣锦,但是定位精確度沒有標(biāo)準(zhǔn)定位服務(wù)高,耗電少氢哮,定位更新頻率依照基站密度而定袋毙,只要在基站范圍內(nèi)就顯示基站位置,當(dāng)進入另一個基站范圍后更新冗尤。
如果要求定位及時听盖,精確度高胀溺,并且運行時間短,可以使用標(biāo)準(zhǔn)定位服務(wù)皆看。
如果長時間監(jiān)控用戶位置仓坞,用戶移動速度較快,可使用顯著位置變化定位服務(wù)
3. 代理方法didUpdateLocations會被持續(xù)調(diào)用腰吟,參數(shù)
manager
位置管理者 locations
表示位置數(shù)組,里面按照時間先后順序存儲CLLocation
對象毛雇,獲取最后一個位置信息[locations lastObject]即可
CLLocationManager -- 關(guān)于定位屬性和方法
// 判斷定位功能是否可用
+ (BOOL)locationServicesEnabled
// 設(shè)置過濾單位(米)即每隔多少米定位一次
@property(assign, nonatomic) CLLocationDistance distanceFilter;
// 設(shè)置定位精確度
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
CLLocationAccuracy kCLLocationAccuracyBestForNavigation // 最適合導(dǎo)航的精確度
CLLocationAccuracy kCLLocationAccuracyBest; // 最好的
CLLocationAccuracy kCLLocationAccuracyNearestTenMeters; // 附近10米范圍內(nèi)
CLLocationAccuracy kCLLocationAccuracyHundredMeters; // 附近100米范圍內(nèi)
CLLocationAccuracy kCLLocationAccuracyKilometer; // 附近1000米范圍內(nèi)
CLLocationAccuracy kCLLocationAccuracyThreeKilometers; // 附近1000米范圍內(nèi)
// 開啟定位
- (void)startUpdatingLocation
// 結(jié)束定位
- (void)stopUpdatingLocation;
CLLocationManagerDelegate -- 定位常用代理方法
// 定位成功 持續(xù)調(diào)用
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
// manager : 位置管理者
// locations : 位置數(shù)組
// 在這里拿到位置信息做一些處理嫉称,這個方法會被持續(xù)調(diào)
}
// 定位失敗時調(diào)用
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// manager : 位置管理者
// error : 錯誤信息
}
// 當(dāng)用戶定位授權(quán)狀態(tài)發(fā)生變化時調(diào)用
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
// manager : 管理者
// status :狀態(tài)
/*
kCLAuthorizationStatusNotDetermined = 0 // 用戶未決定時
kCLAuthorizationStatusRestricted // 受限制保留字段
kCLAuthorizationStatusDenied // 被拒絕 1.被拒絕 2.未開啟定位服務(wù)
kCLAuthorizationStatusAuthorizedAlways // 前后臺都可以定位授權(quán)
kCLAuthorizationStatusAuthorizedWhenInUse // 前臺定位授權(quán)
*/
}
CLLocation對象 -- 定位基本屬性
// 根據(jù)經(jīng)度和維度創(chuàng)建一個CLLocation對象
- (instancetype)initWithLatitude:(CLLocationDegrees)latitude
longitude:(CLLocationDegrees)longitude;
// 經(jīng)緯度晾腔,latitude經(jīng)度 longitude維度
@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;
// 高度位置 可以正面(海拔)或負面(低于海平面)应闯。
@property(readonly, nonatomic) CLLocationDistance altitude;
// 水平精確度纤控,如果是負值表示不可用
@property(readonly, nonatomic) CLLocationAccuracy horizontalAccuracy;
// 垂直精確度,如果是負值表示海拔不可用
@property(readonly, nonatomic) CLLocationAccuracy verticalAccuracy;
// 真北的位置度 取值 0.0 - 359.9 度 0 表示真北
@property(readonly, nonatomic) CLLocationDirection course
// 速度 m/s 負值表示速度無效
@property(readonly, nonatomic) CLLocationSpeed speed
// 定位時間
@property(readonly, nonatomic, copy) NSDate *timestamp;
// 樓層碉纺,如果建筑物注冊船万,可以獲取樓層
@property(readonly, nonatomic, copy, nullable) CLFloor *floor
// 返回位置
@property (nonatomic, readonly, copy) NSString *description;
// 計算兩個坐標(biāo)的物理直線距離
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
2. CLLocationManager -- 設(shè)備方向
手機通過磁力計來判斷設(shè)備方向细层,先看一個簡單指南針的例子
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)CLLocationManager *locationM;
@end
@implementation ViewController
-(CLLocationManager *)locationM
{
if (_locationM == nil) {
_locationM = [[CLLocationManager alloc]init];
_locationM.delegate = self;
}
return _locationM;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.locationM startUpdatingHeading];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
/**
* CLHeading
* magneticHeading : 距離磁北方向的角度
* trueHeading : 距離真北方向的角度
* headingAccuracy : 如果這個值是負數(shù), 那么代表角度不可用
*/
if (newHeading.headingAccuracy < 0) {
return;
}
CGFloat angle = newHeading.magneticHeading;
CGFloat r = angle * M_PI / 180;
[UIView animateWithDuration:0.5 animations:^{
self.imageView.transform = CGAffineTransformMakeRotation(-r);
}];
}
CLLocationManager -- 關(guān)于手機朝向?qū)傩院头椒?/h5>
// 判斷是否支持磁力計定位手機朝向
+ (BOOL)headingAvailable
// 開啟手機朝向定位
- (void)startUpdatingHeading
// 關(guān)閉手機朝向定位
- (void)stopUpdatingHeading
CLLocationManagerDelegaer -- 關(guān)于手機朝向的代理方法
// 當(dāng)獲取一個新朝向的時候調(diào)用,持續(xù)調(diào)用
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
// manager : 管理者
// newHeading : 朝向
}
CLHeading對象基本屬性
// 與磁北方向的角度 范圍 0.0 - 359.9 度, 0 度表示磁北方向
@property(readonly, nonatomic) CLLocationDirection magneticHeading;
// 與真北方向的角度 范圍 0.0 - 359.9 度, 0 度表示真北方向
@property(readonly, nonatomic) CLLocationDirection trueHeading;
// 返回方向值的錯誤范圍唬涧,負值表示無效的朝向
@property(readonly, nonatomic) CLLocationDirection headingAccuracy;
// 返回方向的時間
@property(readonly, nonatomic, copy) NSDate *timestamp;
// 判斷是否支持磁力計定位手機朝向
+ (BOOL)headingAvailable
// 開啟手機朝向定位
- (void)startUpdatingHeading
// 關(guān)閉手機朝向定位
- (void)stopUpdatingHeading
// 當(dāng)獲取一個新朝向的時候調(diào)用,持續(xù)調(diào)用
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
// manager : 管理者
// newHeading : 朝向
}
// 與磁北方向的角度 范圍 0.0 - 359.9 度, 0 度表示磁北方向
@property(readonly, nonatomic) CLLocationDirection magneticHeading;
// 與真北方向的角度 范圍 0.0 - 359.9 度, 0 度表示真北方向
@property(readonly, nonatomic) CLLocationDirection trueHeading;
// 返回方向值的錯誤范圍唬涧,負值表示無效的朝向
@property(readonly, nonatomic) CLLocationDirection headingAccuracy;
// 返回方向的時間
@property(readonly, nonatomic, copy) NSDate *timestamp;
注意:當(dāng)獲取朝向的時候不需要向用戶請求授權(quán)疫赎,因為設(shè)備方向不涉及到用戶隱私
3. CLLocationManager -- 區(qū)域監(jiān)聽
區(qū)域監(jiān)聽實例
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property(nonatomic,strong)CLLocationManager *locationM;
@end
@implementation ViewController
-(CLLocationManager *)locationM
{
if (_locationM == nil) {
_locationM = [[CLLocationManager alloc]init];
_locationM.delegate = self;
[_locationM requestAlwaysAuthorization];
}
return _locationM;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 中心
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(67.666, 80.888);
// 區(qū)域半徑
CLLocationDistance distance = 1000.0;
CLCircularRegion *range = [[CLCircularRegion alloc]initWithCenter:center radius:distance identifier:@"ding"];
// 用這個方法需要有位置的變化才行,從外部進來 或者出去才會有響應(yīng)
// [self.locationM startMonitoringForRegion:range];
// 用這個方法就會先獲取一次碎节,判斷是否在區(qū)域中
[self.locationM requestStateForRegion:range];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@"進入?yún)^(qū)域");
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(@"出去區(qū)域");
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
NSLog(@"%@",region);
// 獲取請求指定區(qū)域狀態(tài)時調(diào)用的方法
/**
CLRegionStateUnknown, // 不知道
CLRegionStateInside, // 在區(qū)域內(nèi)部
CLRegionStateOutside // 在區(qū)域外部
*/
if(state == CLRegionStateInside)
{
NSLog(@"在區(qū)域中");
}else if (state == CLRegionStateOutside)
{
NSLog(@"在區(qū)域外面");
}
}
@end
注意:
[self.locationM startMonitoringForRegion:range];
開啟區(qū)域監(jiān)聽捧搞,需要有位置的變化才會調(diào)用代理方法,例如位置從區(qū)域外部進入?yún)^(qū)域內(nèi)部狮荔。
[self.locationM requestStateForRegion:range];
程序一運行就會先確定在不在區(qū)域中胎撇,當(dāng)位置發(fā)生改變時也會判斷在不在區(qū)域中,是進入?yún)^(qū)域還是離開區(qū)域
CLLocationManager -- 關(guān)于區(qū)域間廳屬性和方法
// 判斷當(dāng)前設(shè)備是否支持區(qū)域監(jiān)聽(區(qū)域類型)
+ (BOOL)isMonitoringAvailableForClass:(Class)regionClass
// 最大的區(qū)域大小殖氏,超過這個最大值后無效
@property (readonly, nonatomic) CLLocationDistance maximumRegionMonitoringDistance
// 開啟一個區(qū)域的監(jiān)聽
- (void)startMonitoringForRegion:(CLRegion *)region
// 請求一個區(qū)域的監(jiān)聽
- (void)requestStateForRegion:(CLRegion *)region
CLLocationManagerDelegaer -- 關(guān)于區(qū)域監(jiān)聽的代理方法
// 進入?yún)^(qū)域時調(diào)用 manager : 管理者 region:區(qū)域
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
state : 狀態(tài)
CLRegionStateUnknown, // 不知道
CLRegionStateInside, // 在區(qū)域內(nèi)部
CLRegionStateOutside // 在區(qū)域外部
}
CLCircularRegion對象基本屬性
CLCircularRegion是CLRegion的子類
// 創(chuàng)建方法 cente : 中心位置 radius : 區(qū)域半徑 identidier : 唯一標(biāo)示
- (instancetype)initWithCenter:(CLLocationCoordinate2D)center
radius:(CLLocationDistance)radius
identifier:(NSString *)identifier;
// 中心位置
@property (readonly, nonatomic) CLLocationCoordinate2D center;
// 半徑
@property (readonly, nonatomic) CLLocationDistance radius;
4. 地理編碼和反地理編碼
地理編碼指 地址轉(zhuǎn)經(jīng)緯度晚树,反地理編碼指 經(jīng)緯度轉(zhuǎn)地址。使用CLGeocoder來獲取雅采。
CLGeocoder 的使用
// 創(chuàng)建
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
// 地理編碼
[self.geocoder geocodeAddressString:地址 completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (error == nil) {
}
}];
// 反地理編碼
[self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (error == nil) {
}
}];
注意:方法中返回的是一個裝著CLPlacemark對象的數(shù)組爵憎,是對輸入地址名稱或者經(jīng)緯度進行檢索的結(jié)果,因此返回多個結(jié)果供選擇婚瓜。error指錯誤信息宝鼓,如果錯誤error有值
CLPlacemark基本屬性
// 對應(yīng)的位置對象 參考CLLocation基本屬性
@property (nonatomic, readonly, copy, nullable) CLLocation *location;
@property (nonatomic, readonly, copy, nullable) CLRegion *region; // 范圍
@property (nonatomic, readonly, copy, nullable) NSTimeZone *timeZone // 時區(qū)
@property (nonatomic, readonly, copy, nullable) NSString *name; // 地址名稱
@property (nonatomic, readonly, copy, nullable) NSString *thoroughfare; // 街道
@property (nonatomic, readonly, copy, nullable) NSString *subThoroughfare; // 街道相關(guān)信息,例如門牌
@property (nonatomic, readonly, copy, nullable) NSString *locality; // 城市
@property (nonatomic, readonly, copy, nullable) NSString *subLocality; // 城市內(nèi)分區(qū)
@property (nonatomic, readonly, copy, nullable) NSString *administrativeArea; // 直轄市
@property (nonatomic, readonly, copy, nullable) NSString *subAdministrativeArea; // 其他行政區(qū)域信息
@property (nonatomic, readonly, copy, nullable) NSString *postalCode; // 郵編
@property (nonatomic, readonly, copy, nullable) NSString *ISOcountryCode; // 國家編碼
@property (nonatomic, readonly, copy, nullable) NSString *country; // 國家
@property (nonatomic, readonly, copy, nullable) NSString *inlandWater; // 水源湖泊
@property (nonatomic, readonly, copy, nullable) NSString *ocean; // 海洋
@property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *areasOfInterest; // 關(guān)聯(lián)的或利益相關(guān)的地標(biāo)
四. iOS9/iOS8/iOS8之前的定位適配
1. iOS8.0之前是默認請求授權(quán)巴刻,需要在plist文件中加入
設(shè)置后臺執(zhí)行
2. iOS 8.0
使用[_locationM requestWhenInUseAuthorization];
請求獲取前臺定位愚铡,
[_locationM requestAlwaysAuthorization];
請求獲取前后臺定位,并修改plist文件胡陪。
iOS8.0以上也可以在Background Modes中設(shè)置后臺定位沥寥,但是當(dāng)后臺定位的時候,會出現(xiàn)一個藍條提醒用戶正在后臺定位
3. iOS 9.0
iOS 9.0 與iOS8.0一樣柠座,唯一的區(qū)別在于邑雅,當(dāng)在Background Modes中設(shè)置后臺定位時,需要_locationM.allowsBackgroundLocationUpdates = YES;
設(shè)置允許愚隧。
并且iOS 9.0中新添加了單次定位的方法[self.locationM requestLocation];
只獲取一次位置信息蒂阱。
實現(xiàn)邏輯:
(1) 按照定位精確度從低到高進行排序,逐個進行定位.如果在有效時間內(nèi), 定位到了精確度最好的位置, 那么就把對應(yīng)的位置通過代理告知外界.
(2) 如果獲取到的位置不是精確度最高的那個狂塘,也會在定位超時后录煤,通過代理告訴外界.
注意事項:
(1) 必須實現(xiàn)代理的-locationManager:didFailWithError:方法
(2) 不能與startUpdatingLocation方法同時使用
五. 第三方框架LocationManager
第三方框架的使用非常簡單,GitHub上已經(jīng)講解的很清晰荞胡。LocationManager是將CLLocationManager由代理向block的封裝轉(zhuǎn)換妈踊。CoreCLLocation使用代理,代碼比較分散泪漂,第三方框架使用block來接收用戶信息廊营,并且額外增加了設(shè)置超時時間等功能歪泳,使用更簡單方便易讀。
文中如果有不對的地方歡迎指出露筒。我是xx_cc呐伞,一只長大很久但還沒有二夠的家伙。