源碼地址
直接上重點(diǎn),先把地址放出來(lái),比較著急的,可以直接拿取用,傳送門(mén):https://github.com/wxh794708907/YJYYLocationManager
前言
首先說(shuō)說(shuō)為什么要封裝這個(gè)饥脑,我記得我做的前幾個(gè)項(xiàng)目中都用到了獲取用戶(hù)定位的功能,當(dāng)時(shí)是基于百度地圖或者是高德地圖來(lái)做的懦冰,所以當(dāng)時(shí)并沒(méi)有涉及到自己去封裝一個(gè)管理類(lèi)的想法灶轰,目前在做的項(xiàng)目中由于沒(méi)有繼承百度地圖/高德地圖但是需要用到用戶(hù)定位、用戶(hù)和主播之間的距離刷钢、用戶(hù)城市等信息笋颤,因此特意封裝了一個(gè)獲取定位、計(jì)算兩個(gè)坐標(biāo)距離等功能的管理類(lèi),防止以后再用到時(shí)都寫(xiě)重復(fù)的代碼伴澄。順便帶著不熟悉CoreLocation框架的簡(jiǎn)友們一起來(lái)封裝一個(gè)工具類(lèi)赋除。
CoreLocation的使用
封裝之前我們還是來(lái)看看不封裝時(shí)CoreLocation怎么用的,如果不會(huì)用那封裝就不用說(shuō)了非凌,不過(guò)這篇文章我不想再談CoreLocation的使用举农,因?yàn)槲以诤芫靡郧皩?xiě)過(guò)一篇文章專(zhuān)門(mén)來(lái)介紹CoreLocation的使用,所以如果各位看官不熟悉CoreLocation使用的敞嗡,先去看看我以前的文章介紹颁糟。傳送門(mén):http://www.reibang.com/p/a0ba17eda84e
封裝第一步 接口的定制
無(wú)論是封裝什么工具類(lèi),第一步我覺(jué)得是考慮好自己的需求喉悴,需要做什么棱貌,然后再根據(jù)需求來(lái)定制接口,就以我們今天的需求來(lái)說(shuō)粥惧,我們是需要獲取用戶(hù)的地理位置键畴,那么我們定制接口的時(shí)候肯定是需要有經(jīng)緯度或者地名之類(lèi)的參數(shù)或者返回值,考慮到使用的簡(jiǎn)便和獲取定位的異步性突雪,這里我們使用block的形式來(lái)做,接口定義如下:
/**
創(chuàng)建地理位置管理單例
@return 返回地理位置管理單例
*/
+ (instancetype)sharedManager;
/**
更新地理位置
@param locationCB 定位成功回調(diào)
*/
- (void)updateLocationWithLocation:(void(^)(CLLocation *location))locationCB failureCB:(void(^)(NSError *error))failureCB;
/**
傳入經(jīng)緯度返回地名
@param location location
@param callBack 反編碼成功回調(diào)
*/
- (void)reverseGeocodeLocation:(CLLocation *)location callBack:(void(^)(NSString * place))callBack;
/**
獲取定位后反編碼之后用戶(hù)位置
@param callBack 反編碼成功之后的回調(diào)
*/
- (void)updateReverseGeocodeLocation:(void(^)(NSString *placeName))callBack;
/**
更新速度信息
@param callBack 速度變化回調(diào)
*/
-(void)updateSpeedWithCallBack:(void(^)(CLLocationSpeed speed)) callBack;
/**
計(jì)算相對(duì)距離(km)
@param startCoordinate 起點(diǎn)
@param targetCoordinate 目標(biāo)位置
@return 相對(duì)距離
*/
-(CLLocationDistance)distanceFromCoordinate:(CLLocationCoordinate2D)startCoordinate
toTargetCoordinate:(CLLocationCoordinate2D) targetCoordinate;
/**
計(jì)算當(dāng)前位置到目標(biāo)位置的相對(duì)距離(km)
@param targetCoordinate 目標(biāo)位置
*/
-(void)distanceFromCurrentLocationToTargetLocation:(CLLocationCoordinate2D) targetCoordinate callBack:(void(^)(CLLocationDistance distance))callBack;
當(dāng)然這里我們還必須有管理單例的創(chuàng)建接口涡贱,此外我們?cè)讷@取定位失敗時(shí)也需要有回調(diào)咏删,所以這里我們還加入了失敗的回調(diào)。
封裝第二部 接口的實(shí)現(xiàn)
接口的定制我們已經(jīng)做完了问词,接下來(lái)就該來(lái)實(shí)現(xiàn)了督函,我們獲取定位是根據(jù)蘋(píng)果的代理回調(diào)來(lái)做的,但是我們?cè)谶@個(gè)方法實(shí)現(xiàn)里是獲取不到定位的激挪,怎么辦辰狡?添加全局屬性來(lái)關(guān)聯(lián)啊。具體實(shí)現(xiàn)如下:
#import "YJYYLocationManager.h"
#import <CoreLocation/CoreLocation.h>
typedef void(^YJYYLocationBlock)(CLLocation *location);
typedef void(^YJYYFailureBlock)(NSError *error);
typedef void(^YJYYReverseGeoBlock)(NSString *placeName);
typedef void(^YJYYSpeedBlock)(CLLocationSpeed speed);
@interface YJYYLocationManager ()<CLLocationManagerDelegate>{
// 位置管理
CLLocationManager *_locationManager;
// 地理反編碼
CLGeocoder *_geocoder;
}
/** 定位成功block */
@property(nonatomic,copy) YJYYLocationBlock locationBlock;
/** 定位失敗block */
@property(nonatomic,copy) YJYYFailureBlock failureBlock;
/** 反編碼成功block */
@property(nonatomic,copy) YJYYReverseGeoBlock reverseGeoBlcok;
/** 速度變化block */
@property(nonatomic,copy) YJYYSpeedBlock speedBlock;
@end
@implementation YJYYLocationManager
+ (instancetype)sharedManager {
static id _instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (instancetype)init {
if (self = [super init]) {
_geocoder = [[CLGeocoder alloc] init];
// 創(chuàng)建管理者
_locationManager = [[CLLocationManager alloc] init];
// 設(shè)置代理
_locationManager.delegate = self;
// 設(shè)置定位距離過(guò)濾參數(shù) (當(dāng)本次定位和上次定位之間的距離大于或等于這個(gè)值時(shí)垄分,調(diào)用代理方法)
_locationManager.distanceFilter = 100 ;
// 設(shè)置定位精度(精度越高越耗電)
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
//版本適配 請(qǐng)求權(quán)限 iOS 8之后必須在plist中添加相應(yīng)key
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestWhenInUseAuthorization];
}
//判斷是否允許定位
if ([CLLocationManager locationServicesEnabled]) {
// 開(kāi)啟實(shí)時(shí)定位
[_locationManager startUpdatingLocation];
}
}
return self;
}
//獲取用戶(hù)定位
- (void)updateLocationWithLocation:(void (^)(CLLocation *location))locationCB failureCB:(void (^)(NSError *error))failureCB{
NSAssert(locationCB != nil, @"定位成功回調(diào)不能為空");
self.locationBlock = locationCB;
NSAssert(failureCB != nil, @"定位失敗回調(diào)不能為空");
self.failureBlock = failureCB;
}
//反地理編碼
- (void)reverseGeocodeLocation:(CLLocation *)location callBack:(void(^)(NSString * place))callBack{
[_geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
CLPlacemark * placemark = placemarks.firstObject;
if (callBack) {
callBack(placemark.name);
}
}];
}
//獲取定位后再反編碼
- (void)updateReverseGeocodeLocation:(void (^)(NSString *placeName))callBack {
NSAssert(callBack !=nil, @"反編碼回調(diào)傳入不能為空");
self.reverseGeoBlcok = callBack;
}
//用戶(hù)速度回調(diào)
- (void)updateSpeedWithCallBack:(void (^)(CLLocationSpeed speed))callBack {
NSAssert(callBack !=nil, @"速度回調(diào)傳入不能為空");
self.speedBlock = callBack;
}
//計(jì)算兩坐標(biāo)距離
- (CLLocationDistance)distanceFromCoordinate:(CLLocationCoordinate2D)startCoordinate toTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate {
CLLocation *curtLocation = [[CLLocation alloc] initWithLatitude:startCoordinate.latitude longitude:startCoordinate.longitude];
CLLocation *targetLocation = [[CLLocation alloc] initWithLatitude:targetCoordinate.latitude longitude:targetCoordinate.longitude];
CLLocationDistance distance = [curtLocation distanceFromLocation:targetLocation];
return distance/1000.f;
}
//從當(dāng)前位置到目標(biāo)位置距離
- (void)distanceFromCurrentLocationToTargetLocation:(CLLocationCoordinate2D)targetCoordinate callBack:(void (^)(CLLocationDistance))callBack{
//先獲取定位
[self updateLocationWithLocation:^(CLLocation *location) {
if (callBack) {
callBack([self distanceFromCoordinate:location.coordinate toTargetCoordinate:targetCoordinate]);
}
} failureCB:^(NSError *error) {
NSLog(@"抱歉獲取定位失敗");
}];
}
#pragma mark - 代理回調(diào)
#pragma mark -
/** 獲取到新的位置信息時(shí)調(diào)用*/
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//去除第一條數(shù)據(jù)
CLLocation * location = locations.firstObject;
__weak typeof(self) weakSelf = self;
//定位成功回調(diào)
if (self.locationBlock) {
self.locationBlock(locations.firstObject);
}
//反編碼回調(diào)
if (self.reverseGeoBlcok) {
[self reverseGeocodeLocation:locations.firstObject callBack:^(NSString *place) {
weakSelf.reverseGeoBlcok(place);
}];
}
//速度回調(diào)
if (self.speedBlock) {
self.speedBlock(location.speed *3.6);
}
}
/** 不能獲取位置信息時(shí)調(diào)用*/
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if (self.failureBlock) {
NSLog(@"獲取定位失敗");
self.failureBlock(error);
}
}
@end
代碼都有詳細(xì)的注釋 還是很清晰的宛篇,有什么不懂得可以留言
總結(jié)
封裝一個(gè)工具類(lèi),我覺(jué)得核心思想就是明確自己需要的薄湿,然后根據(jù)想要的去定制接口 叫倍,最后想辦法來(lái)實(shí)現(xiàn)需求,當(dāng)然除了作為一個(gè)工具類(lèi)來(lái)說(shuō)豺瘤,就單單返回用戶(hù)定位這一個(gè)方法肯定是不夠的吆倦,我還提供了更加豐富的API在里面例如地理反編碼( 傳入經(jīng)緯度返回地名)、 獲取定位后反編碼之后用戶(hù)位置坐求、 更新速度信息等 具體可以查看代碼或者看我的github地址蚕泽。有需要的可以直接拿去用。哪里寫(xiě)的不好的地方也可以提出更好的建議桥嗤。