iOS封装定位类库,可自定义采集精度、超时时间、有效距离等功能

github地址:https://github.com/longitachi/GPSLocationPicker 

博客资源下载地址:http://download.csdn.net/detail/longitachi/9174445


本文所使用的定位为iOS系统原生定位,使用前需导入 CoreLocation.framework框架

现在越来越多的项目中都会或多或少的使用到定位功能,而有些时候并不是拿到了用户的当前坐标就可以了,而是需要对采集到的坐标进行一个 有效精度,和有效距离的判断。比如某些企业级软件应用,用户进行考勤时候,则需要对精度和有效距离做一个较为精确的控制。

针对这些需求,进行设计,主要设计思想是将定位类分为两大块,分别为 GPSLocationPickerGPSValidLocaitionPicker


GPSLocationPicker主要负责底层的定位,只满足用户对有效精度的限制,无等待对话框显示;

GPSValidLocaitionPicker 则负责对用户额外属性的扩展,如定位超时时长、有效精度、有效距离等,且该类中有等待对话框,并可以根据用户设置的是否显示GPS详情进行显示。


不显示定位详细信息的效果图


显示定位详细信息的效果图



下面先说下GPSLocationPicker

该类的.h文件中主要提供以下参数及接口

#define kZeroLocation [[CLLocation alloc] initWithLatitude:0 longitude:0]
#define kLocationFailedError [NSError errorWithDomain:@"location failed" code:-100 userInfo:nil]

typedef void (^LocationResult)(CLLocation *location, NSError *error);

@interface GPSLocationPicker : NSObject

//定位期望精度(单位:m),默认为-1,不要求采集精度,则拿到坐标直接回调
@property (nonatomic, assign) CLLocationAccuracy precision;

+ (instancetype)shareGPSLocationPicker;
/**
 * @brief 启动定位,并设置定位成功回调
 */
- (void)startLocationAndCompletion:(LocationResult)completion;

/**
 * @brief 停止定位
 */
- (void)stop;

@end

.m中对应的实现:

#import "GPSLocationPicker.h"

@interface GPSLocationPicker () <CLLocationManagerDelegate>
{
    CLLocationManager *_locationManager;
    LocationResult _locationResultBlock;
    BOOL _isStop;
}

@end

@implementation GPSLocationPicker

static GPSLocationPicker *picker = nil;

+ (instancetype)shareGPSLocationPicker
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        picker = [[GPSLocationPicker alloc] init];
    });
    return picker;
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{   //onceToken是GCD用来记录是否执行过 ,如果已经执行过就不再执行(保证执行一次)
        picker = [super allocWithZone:zone];
    });
    return picker;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _precision = -1;
    }
    return self;
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
    switch (status) {
        case kCLAuthorizationStatusNotDetermined:
            if ([[[UIDevice currentDevice] systemVersion] doubleValue] > 8.0) {
                [_locationManager requestWhenInUseAuthorization];
            } else {
                [_locationManager startUpdatingLocation];
            }
            break;
        case kCLAuthorizationStatusDenied:
            //请打开系统设置中\"隐私->定位服务\",允许使用您的位置。
            break;
        case kCLAuthorizationStatusRestricted:
            //定位服务无法使用!
            break;
            
        default:
            [_locationManager startUpdatingLocation];//开启位置更新
            break;
    }
}

#pragma mark - 启动定位
- (void)startLocationAndCompletion:(LocationResult)completion
{
    //初始化CLLocationManager
    if (_locationManager) {
        _isStop = NO;
        _locationManager = nil;
    }
    if (_locationResultBlock) {
        _locationResultBlock = nil;
    }
    NSLog(@"启动定位");
    
    _locationResultBlock = completion;
    
    _locationManager = [[CLLocationManager alloc] init];
    _locationManager.delegate = self;
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    _locationManager.distanceFilter = kCLDistanceFilterNone;
    if ([[[UIDevice currentDevice] systemVersion] doubleValue] > 8.0)
    {
        [_locationManager requestAlwaysAuthorization];// 前后台同时定位
    }
    [_locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    if (_isStop) {
        return;
    }
    
    //得到Location
    CLLocation *coord = [locations objectAtIndex:0];
    
    NSLog(@"采集到的坐标经度:%f, 维度:%f  精度%f", coord.coordinate.longitude, coord.coordinate.latitude, coord.horizontalAccuracy);
    //判断采集到的精度
    if (_locationResultBlock && !_isStop && (self.precision == -1 || coord.horizontalAccuracy <= self.precision)) {
        _locationResultBlock(coord, nil);
    }
}

#pragma mark - 定位失败
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"定位失败:%@", error);
    if (!_isStop && _precision == -1 && _locationResultBlock) {
        _locationResultBlock(kZeroLocation, error);
    }
}

- (void)stop
{
    _isStop = YES;
    [_locationManager stopUpdatingLocation];
}

以上则为对  GPSLocationPicker 的全部实现,用户如果不需要对有效距离进行限制则可以直接调用该类,调用方法为

    //设置有效精度

    [GPSLocationPickershareGPSLocationPicker].precision = -1;

    [[GPSLocationPickershareGPSLocationPicker]startLocationAndCompletion:^(CLLocation *location,NSError *error) {

        // your code here

    }];


在介绍下GPSValidLocaitionPicker的实现:

该类的.h文件中主要提供以下参数及接口

<span style="font-size:12px;">#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>

#define kIsShowGPSDetailInfo @"isShowDetailInfo"

typedef void (^ValidLocationResult)(CLLocation *location, NSError *error);

@interface GPSValidLocationPicker : NSObject

//定位超时时间(单位:s), 默认为-100
@property (nonatomic, assign) int timeoutPeriod;
//定位期望精度(单位:m),默认为-100
@property (nonatomic, assign) CLLocationAccuracy precision;
//当前坐标
@property (nonatomic, assign) CLLocationCoordinate2D nowCoordinate;
//定位有效距离(单位:m),默认为-100
@property (nonatomic, assign) CLLocationDistance validDistance;


+ (instancetype)shareGPSValidLocationPicker;
/**
 * @brief 启动定位,如果需要对超时时长、采集到的坐标精度和有效距离做要求, 则需要在调用该方法之前对 timeoutPeriod, precision, nowCoordinate, valiDistance 做一个对应的赋值
 */
- (void)startLocationAndCompletion:(ValidLocationResult)completion;

/**
 * @brief 重置变量为默认值
 */
- (void)resetDefaultVariable;

@end</span><u style="font-size:18px;">
</u>

.m中对应实现

#import "GPSValidLocationPicker.h"
#import "GPSLocationPicker.h"
#import "MBProgressHUD.h"
#import "MBProgressHUD+DetailLabelAlignment.h"

#define kDefaultValue -100

@interface GPSValidLocationPicker () <CLLocationManagerDelegate, MBProgressHUDDelegate>
{
    CLLocationAccuracy _nowPrecision;//定位拿到的精度
    CLLocationDistance _collectDistance;//当前采集到的点与用户传进来的点的距离
    NSTimer *_timer;
    
    MBProgressHUD *_waitView;
    int _totalTime;
    ValidLocationResult _locationResultBlock;
}

@end

@implementation GPSValidLocationPicker

static GPSValidLocationPicker *_ValidLocationPicker = nil;

+ (instancetype)shareGPSValidLocationPicker
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _ValidLocationPicker = [[self alloc] init];
    });
    return _ValidLocationPicker;
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _ValidLocationPicker = [super allocWithZone:zone];
    });
    return _ValidLocationPicker;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self resetDefaultVariable];
    }
    return self;
}

- (void)resetDefaultVariable
{
    _nowPrecision = _timeoutPeriod = _precision = _validDistance = _collectDistance = kDefaultValue;
}

#pragma mark - 启动定位
- (void)startLocationAndCompletion:(ValidLocationResult)completion
{
    if (_locationResultBlock) {
        _locationResultBlock = nil;
    }
    _locationResultBlock = completion;
    
    _totalTime = self.timeoutPeriod;
    //显示等待视图
    [self beginWaiting:@"定位中,请稍后。。。" mode:_totalTime>0?MBProgressHUDModeDeterminateHorizontalBar:MBProgressHUDModeIndeterminate];
    
    if (_timeoutPeriod > 0) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    }
    
    [self startGetLocation];
}

- (void)startGetLocation
{
    //这里设置为-1,使GPSLocationPicker拿到坐标后直接回调
    [GPSLocationPicker shareGPSLocationPicker].precision = -1;
    [[GPSLocationPicker shareGPSLocationPicker] startLocationAndCompletion:^(CLLocation *location, NSError *error) {
        [self judgeNowLocationIsValid:location];
    }];
}

#pragma mark - 判断当前采集到的坐标是否符合标准
- (void)judgeNowLocationIsValid:(CLLocation *)pickCoord
{
    _nowPrecision = pickCoord.horizontalAccuracy;
    
    //首先判断坐标是否有效
    if ( pickCoord.coordinate.latitude == 0 || pickCoord.coordinate.longitude == 0) {
        return;
    }
    
    if (_precision == kDefaultValue && _validDistance == kDefaultValue) {
        //没有精度和有效距离的限制,当前坐标有效
        [self locationSuccess:pickCoord];
        return;
    }
    
    BOOL coordIsValid = YES;
    
    if (_precision != kDefaultValue && _nowPrecision >= _precision) {
        coordIsValid = NO;
    }
    if (_validDistance != kDefaultValue
        && _nowCoordinate.latitude != 0
        && _nowCoordinate.longitude != 0
        && [self coordIsValid:pickCoord] == NO) {
        coordIsValid = NO;
    }
    //如果坐标符合期望精度及有效距离
    if (coordIsValid) {
        NSLog(@"符合了标准:%d", coordIsValid);
        [self locationSuccess:pickCoord];
    }
}

#pragma mark - 显示更新定位进度
- (void)updateProgress
{
    if (_timeoutPeriod == -1) {
        //定位超时
        [self locationTimeOut:[NSError errorWithDomain:@"location failed" code:-1 userInfo:nil]];
        return;
    }
    
    if (!_waitView) {
        [self beginWaiting:@"定位中,请稍后。。。" mode:MBProgressHUDModeDeterminateHorizontalBar];
    }
    _timeoutPeriod--;
   _waitView.detailsLabelText = [self getGPSDetailInfo];
    if (_totalTime > 0) {
        _waitView.progress = (float)(_totalTime-_timeoutPeriod)/_totalTime;
    }
}

#pragma mark - 拿到符合标准的坐标
- (void)locationSuccess:(CLLocation *)coord
{
    NSLog(@"%s", __FUNCTION__);
    [_timer invalidate];
    [_waitView hide:YES];
    [[GPSLocationPicker shareGPSLocationPicker] stop];
    if (_locationResultBlock) {
        _locationResultBlock(coord, nil);
    }
}

#pragma mark - 定位超时
- (void)locationTimeOut:(NSError *)error
{
    NSLog(@"%s", __FUNCTION__);
    [_timer invalidate];
    [[GPSLocationPicker shareGPSLocationPicker] stop];
    [_waitView hide:YES];
    if (_locationResultBlock) {
        _locationResultBlock(kZeroLocation, kLocationFailedError);
    }
}

- (NSString *)getGPSDetailInfo
{
    //是否显示gps详情 (根据用户需要进行设置)
    BOOL isShowGPSDetail = [[NSUserDefaults standardUserDefaults] boolForKey:kIsShowGPSDetailInfo];
    
    NSMutableString *detailStr = [NSMutableString string];
    if (_timeoutPeriod != kDefaultValue) {
        if (_timeoutPeriod >= 0) {
            [detailStr appendString:[NSString stringWithFormat:@"等待时间:%d", _timeoutPeriod]];
        } else {
            [detailStr appendString:@"定位超时"];
        }
    }
    if (_precision != kDefaultValue && isShowGPSDetail) {
        [detailStr appendString:[NSString stringWithFormat:@"\n标准精度:%.0f米", self.precision]];
        if (_nowPrecision != kDefaultValue) {
            [detailStr appendString:[NSString stringWithFormat:@"\n当前精度:%.0f米", _nowPrecision]];
        }
    }
    if (_validDistance != kDefaultValue && isShowGPSDetail) {
        [detailStr appendString:[NSString stringWithFormat:@"\n标准距离:%.0f米", self.validDistance]];
        if (_collectDistance != kDefaultValue) {
            [detailStr appendString:[NSString stringWithFormat:@"\n当前距离:%.0f米", _collectDistance]];
        } else {
            [detailStr appendFormat:@"\n当前距离∞米"];
        }
    }
    return detailStr.length == 0 ? @"" : detailStr;
}

#pragma mark - 计算采集到的坐标与传入坐标距离是否符合标准
- (BOOL)coordIsValid:(CLLocation *)nowLocation
{
    if (nowLocation.coordinate.latitude == 0 || nowLocation.coordinate.longitude == 0) {
        return NO;
    }
    CLLocation *lastLocation = [[CLLocation alloc] initWithLatitude:self.nowCoordinate.latitude longitude:self.nowCoordinate.longitude];
    
    CLLocationDistance distance = [nowLocation distanceFromLocation:lastLocation];
    
    _collectDistance = distance;
    NSLog(@"距离:%f", distance);
    if (distance <= _validDistance) {
        return YES;
    } else {
        return NO;
    }
}

#pragma mark - 显示等待视图
-(void)beginWaiting:(NSString *)message mode:(MBProgressHUDMode)mode
{
    if (!_waitView) {
        _waitView = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].keyWindow];
        [[UIApplication sharedApplication].keyWindow addSubview:_waitView];
        _waitView.delegate = self;
        _waitView.square = NO;
        _waitView.mode = mode;
        [_waitView setDetailLabelAlignment:NSTextAlignmentLeft];
    }
    
    _waitView.labelText = message;
    _waitView.detailsLabelText = [self getGPSDetailInfo];
    [_waitView show:YES];
}

- (void)hudWasHidden:(MBProgressHUD *)hud
{
    [hud removeFromSuperview];
    _waitView = nil;
}

@end

GPSValidLocaitionPicker类中使用到了第三方库MBProgressHUD, 为了显示定位详情时的信息左对齐又不影响原三方库,我为其添加了类别 MBProgressHUD+DetailLabelAlignment.h,实现非常简单,不过唯一需要注意的一点就是 MBProgressHUD原有的detailsLabel在.m文件中,外界无法调用,我把它移动到了.h中

//这里为了方便设置MBProgressHUD的detailLabel的对齐方式,把MBProgressHUD类中的detailsLabel属性从.m中移动到了.h中

@interface MBProgressHUD (DetailLabelAlignment)

/**
 * @brief 设置详情label对齐方式
 */
- (void)setDetailLabelAlignment:(NSTextAlignment)alignment;

@end

#import "MBProgressHUD+DetailLabelAlignment.h"

@implementation MBProgressHUD (DetailLabelAlignment)

- (void)setDetailLabelAlignment:(NSTextAlignment)alignment
{
    self->detailsLabel.textAlignment = alignment;
}

@end

到这里整个 GPSValidLocaitionPicker 便实现完成,调用方法为

    GPSValidLocationPicker *gpsPicker = [GPSValidLocationPickershareGPSValidLocationPicker];

   //因为该类设计为单例模式,所以如果多处用则可能出现有些地方设置了变量值保留的问题,所以尽量在调用定位前进行一次重置

    [gpsPicker resetDefaultVariable];

    //测试用值

    gpsPicker.timeoutPeriod =20;

    gpsPicker.precision =10;

    gpsPicker.validDistance =100;

    CLLocationCoordinate2D coord =CLLocationCoordinate2DMake(31.13,121.33);

    gpsPicker.nowCoordinate = coord;

    [gpsPicker startLocationAndCompletion:^(CLLocation *location,NSError *error) {

        if (error) {

            _textView.text = [NSStringstringWithFormat:@"未采集到符合精度的坐标,错误信息:%@", error];

            NSLog(@"未采集到符合精度的坐标,错误信息:%@", error);

        } else {

            _textView.text = [NSStringstringWithFormat:@"采集到符合精度的坐标经度%f,维度%f", location.coordinate.longitude, location.coordinate.latitude];

            NSLog(@"采集到符合精度的坐标经度%f,维度%f", location.coordinate.longitude, location.coordinate.latitude);

        }

    }];


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值