Mr.Peng对CLLocationManager封装

807 阅读3分钟

前言

现在很多app都是根据用户所在行政区域才展示数据,当然我也碰到了不少这样项目。现在我把自己对CLLocationManager封装的抽离出来了,如果你项目中对LocationManager有以下几个特点,那么可以直接拿去用。

1. 只需要成功定位一次;
2. 只需要拿到坐标或者行政区域;
3. 权限改变后,有对应的提示;

话不多说,直接上码。

思路

整个项目中,基本上很少用delegate,一般都是使用block,如果不习惯的可以自行替换。整体的封装思路如下:

1. 创建单例;
2. 创建3个`block`:
    *   `PRLocationCurrentAuthStatus`:当前权限;
    *  `PRLocationSuccess`:成功回调
    *  `PRLocationFaile`:失败回调
3. 调用定位前判断是否有权限;
4. 回调回来的坐标通过`CLGeocoder`的`- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;`方法进行反地理编码

代码

首先创建一个Subclass ofCLLocationManagerPRLocationManager的类,在.h里面

typedef void (^PRLocationCurrentAuthStatus)(BOOL isEnableLocation);

typedef void (^PRLocationSuccess)(CLLocation *location, CLPlacemark *placemark);

typedef void (^PRLocationFaile)(NSError *error);

@interface PRLocationManager : CLLocationManager

@property (nonatomic, copy) PRLocationCurrentAuthStatus currentAuthStatus;

/**

 单例

 **@return** 单例

 */

+ (PRLocationManager *)shareManager;

/**

 判断能否定位

 **@return** YES 可以定位 NO 不能定位

 */

- (BOOL)isEnabledLocation;

/**

 开始定位

 */

- (void)startLocation;

/**

 停止定位,开始后要手动停止

 */

- (void)stopLocation;

/**

 回调结果

 **@param** success 成功回调

 **@param** faile 失败回调

 */

- (void)locationStartSuccess:(PRLocationSuccess)success faile:(PRLocationFaile)faile;

/**

 打开设置

 */

+ (void)openLocationService;

.m里面

@interface  PRLocationManager () <CLLocationManagerDelegate>

@property (nonatomic, copy) PRLocationSuccess successBlock;

@property (nonatomic, copy) PRLocationFaile errorBlock;

@end

@implementation PRLocationManager

#pragma mark ^^^^^^ 单例 ^^^^^^

+ (PRLocationManager *)shareManager {

 static PRLocationManager *shareObject;

 static dispatch_once_t onceToken;

 dispatch_once(&onceToken, ^{

 shareObject = [[PRLocationManager alloc] init];

 });

 return shareObject;

}

#pragma mark ^^^^^^ Init ^^^^^^

- (instancetype)init {

 if ([super init]) {

 self.delegate = self;

  // 设置位置精确度 最佳精确度

  self.desiredAccuracy = kCLLocationAccuracyBest;

  // 设置位置距离精确度

  self.distanceFilter = 10.0;

  // 始终允许访问位置信息

 if ([self respondsToSelector:@selector(requestAlwaysAuthorization)]) {

 [self  requestAlwaysAuthorization];

 }

  // 使用应用程序期间允许访问位置数据

 if ([self respondsToSelector:@selector(requestWhenInUseAuthorization)]) {

 [self  requestWhenInUseAuthorization];

 }

 }

  return  self;

}

#pragma mark ^^^^^^ Method ^^^^^^

// 判断定位操作是否被允许

- (BOOL)isEnabledLocation {

  if ([CLLocationManager  locationServicesEnabled]) {

 return YES;

 }

  return  NO;

}

// 定位开始

- (void)startLocation {

  if ([self  isEnabledLocation]) {

 NSLog(@"开始定位");

 [self  startUpdatingLocation];

 }

}

// 开始定位

- (void)locationStartSuccess:(PRLocationSuccess)success faile:(PRLocationFaile)faile {

 self.successBlock = [success copy];

 self.errorBlock = [faile copy];

 [self  startLocation];

}

// 定位停止

- (void)stopLocation {

  NSLog(@"结束定位");

 [self  stopUpdatingLocation];

}

#pragma mark - CLLocationManagerDelegate

#pragma mark ^^^^^^ 开始定位 ^^^^^^

// 获取定位信息

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {

  // 获取最新的位置

 CLLocation *currentLocation = [locations lastObject];

  // 纬度:latitude 经度:longitude

 NSLog(@"纬度=%f,经度=%f", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude);

  // 初始化反地理编码 geocoder

  CLGeocoder *geocoder = [[CLGeocoder  alloc] init];

  // 根据经纬度反向地理编译出地址信息

 [geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *array, NSError *error) {

 if (array.count > 0) {

 CLPlacemark *placemark = [array objectAtIndex:0];

 !self.successBlock ?: self.successBlock(currentLocation, placemark);

 } else {

 if (([error code] == kCLErrorDenied)) {

 !self.currentAuthStatus ? (!self.errorBlock ?: self.errorBlock(error)) : self.currentAuthStatus(NO); // 如果self.currentAuthStatus没有回调,那么就回调self.errorBlock

 } else {

 !self.errorBlock ?: self.errorBlock(error);

 }

 }

 }];

  // 如果只需要获取一次,那么手动调用停止

  // 如果想持续获取,那么注释掉此行代码

 [self  stopLocation];

}

#pragma mark ^^^^^^ 定位失败 ^^^^^^

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {

 if (([error code] == kCLErrorDenied)) {

 !self.currentAuthStatus ? (!self.errorBlock ?: self.errorBlock(error)) : self.currentAuthStatus(NO); // 如果self.currentAuthStatus没有回调,那么就回调self.errorBlock

 } else {

 !self.errorBlock ?: self.errorBlock(error);

 }

 [self  stopLocation];

}

#pragma mark ^^^^^^ 授权改变 ^^^^^^

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {

  if (status == kCLAuthorizationStatusAuthorizedWhenInUse ||

 status == kCLAuthorizationStatusNotDetermined ||

 status == kCLAuthorizationStatusAuthorizedAlways) {

 !self.currentAuthStatus ?: self.currentAuthStatus(YES); // 可以定位

 } else {

 !self.currentAuthStatus ?: self.currentAuthStatus(NO); // 不能定位

 }

}

#pragma mark ^^^^^^ 打开设置 ^^^^^^

+ (void)openLocationService {

  NSURL *url = [NSURL  URLWithString:UIApplicationOpenSettingsURLString];

  if ([[UIApplication  sharedApplication] canOpenURL:url]) {

 if (@available(iOS 10.0, *)) {

 [[UIApplication  sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@""}  completionHandler:nil];

 } else{

 [[UIApplication  sharedApplication] openURL:url];

 }

 }

}

调用

self.resultLabel.text = @"定位中...";

 [[PRLocationManager  shareManager] locationStartSuccess:^(CLLocation *location, CLPlacemark *placemark) {

 // 获取城市

 NSString *city = placemark.locality;

 if (!city) {

  // 四大直辖市的城市信息无法通过locality获得,只能通过获取省份的方法来获得(如果city为空,则可知为直辖市)

 city = placemark.administrativeArea;

 }

 NSLog(@"city:%@",city);

  self.resultLabel.text = [NSString  stringWithFormat:@"定位成功:%@",city];

 } faile:^(NSError *error) {

 if (error) {

 if (error.code == 2) {

 self.resultLabel.text = @"网络状态不好,请重试";

 NSLog(@"网络状态不好,请重试");

 } else {

 self.resultLabel.text = [NSString stringWithFormat:@"查询错误 = %@", error];

 }

 } else {

 self.resultLabel.text = @"查询失败,没有结果";

 }

 }];

 [PRLocationManager  shareManager].currentAuthStatus = ^(BOOL isEnableLocation) {

 if (isEnableLocation) {

 self.resultLabel.text = @"当前权限,可以定位";

 } else {

  UIAlertController *alertVC = [UIAlertController  alertControllerWithTitle:@"定位失败" message:@"请前往设置,打开定位权限" preferredStyle:UIAlertControllerStyleAlert];

  UIAlertAction *cancelAction = [UIAlertAction  actionWithTitle:@"取消"  style:UIAlertActionStyleCancel  handler:^(UIAlertAction * _Nonnull action) {

 //

 }];

  UIAlertAction *sureAction = [UIAlertAction  actionWithTitle:@"前往"  style:UIAlertActionStyleDestructive  handler:^(UIAlertAction * _Nonnull action) {

 [PRLocationManager  openLocationService];

 }];

 [alertVC addAction:cancelAction];

 [alertVC addAction:sureAction];

 [self  presentViewController:alertVC animated:YES  completion:nil];

  self.resultLabel.text = @"当前权限,不能定位,请打开权限";

 }

 };

GIF

未命名

Github地址

PRLocationManager