美文网首页
iOS CLLocationManager 地理定位

iOS CLLocationManager 地理定位

作者: rainbowboy | 来源:发表于2018-11-27 16:04 被阅读21次

    鉴于地理位置的获取是异步的,所以在获取地理位置信息之后再通知当前用户使用,就封装了下。

    需要导入的头文件:

    import <CoreLocation/CoreLocation.h>

    涉及到的类

    CCLocation
    CClocationManager

    CCLocationManager像个坐标的管理者。CCLocation可以理解为对坐标的一些信息的封装。

    封装的文件

    .h文件

    #import <Foundation/Foundation.h>
    #import <CoreLocation/CoreLocation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    
    /**
     地理位置获取之后的回调block
    
    
     @param isAuthorized 是否授权
     @param coordinate 获取CLLocation的属性coordinate坐标信息,是个结构体,内部包含经纬度。当为kCLLocationCoordinate2DInvalid表示未定位
     @param error 定位失败时返回的信息,成功时为nil
     */
    typedef void(^LocationCallBackWithCoordinateBlock) (BOOL isAuthorized,CLLocationCoordinate2D coordinate ,  NSError * _Nullable error);
    
    /**
     地理位置获取之后的回调block
     
     
     @param isAuthorized 是否授权
     @param location 获取的CLLocation,失败时为nil
     @param error 定位失败时返回的信息,成功时为nil
     */
    typedef void(^LocationCallBackWithLocationBlock) (BOOL isAuthorized,CLLocation * _Nullable location ,  NSError * _Nullable error);
    
    @interface RainLocationKit : NSObject
    
    
    /**
     请求对地理位置获取的授权
     第一次创建kit对象时,会做授权操作,授权结束后我们会去做一次定位。但是授权结束后,不会再去做定位了。如果再想拿同一个对象获取获取定位信息,请使用开始定位来获取
     */
    - (void)requestLocationAuthorized;
    
    
    /**
     开启地理位置获取,并且传递地理位置获取成功后的回调block
    
     因为地理位置的获取是异步的,所以等拿到数据之后再上传
     
    
     @param locationCallBack 地理位置获取成功后的回调
     */
    - (void)startUpdateLocationWithCoordinateCompletion:(LocationCallBackWithCoordinateBlock) locationCallBack;
    
    - (void)startUpdateLocationWithLocationCompletion:(LocationCallBackWithLocationBlock) locationCallBack;
    
    
    /**
     设置定位成功之后回调,通过回调获取数据
    
     @param locationCallBack 回调block
     */
    - (void)setLocationCallbackBlock:(LocationCallBackWithCoordinateBlock) locationCallBack;
    
    - (void)setLocationWithLocationCallBackBlock:(LocationCallBackWithLocationBlock) locationCallBack;
    
    /**
     开始更新定位信息。当授权完成之后,仍然使用当前定位对象时,可以使用该方法更新定位数据
     */
    - (void)startUpdateLocation;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    .m文件

    #import "RainLocationKit.h"
    @interface RainLocationKit()<CLLocationManagerDelegate>
    
    @property (nonatomic, strong, readonly)CLLocationManager *locationManager;
    @property (nonatomic, strong, readonly)CLLocation *location;
    @property (nonatomic, copy)LocationCallBackWithCoordinateBlock locationCallbackBlock;
    @property (nonatomic, copy)LocationCallBackWithLocationBlock locationWithLocationCallBackBlock;
    @end
    
    
    
    @implementation RainLocationKit
    @synthesize locationManager = _locationManager;
    @synthesize location = _location;
    
    - (void)startUpdateLocationWithCoordinateCompletion:(LocationCallBackWithCoordinateBlock) locationCallBack {
        
        self.locationCallbackBlock = locationCallBack;
        
        [self requestLocationAuthorized];
      
    }
    
    - (void)startUpdateLocationWithLocationCompletion:(LocationCallBackWithLocationBlock) locationCallBack {
        self.locationWithLocationCallBackBlock = locationCallBack;
        
        [self requestLocationAuthorized];
    }
    
    - (void)startUpdateLocation {
        
        if ([self canUseLocation]) {
            [self.locationManager startUpdatingLocation];
        }else {
            if (self.locationCallbackBlock) {
                self.locationCallbackBlock(NO, kCLLocationCoordinate2DInvalid, nil);
            }
            
            if (self.locationWithLocationCallBackBlock) {
                self.locationWithLocationCallBackBlock(NO, nil, nil);
            }
        }
        
    }
    
    - (void)requestLocationAuthorized {
        
        if ([CLLocationManager locationServicesEnabled]) {
            if(@available(iOS 8.0,*)) {
                [self.locationManager requestWhenInUseAuthorization];
            }
        }else {
            
    #ifdef DEBUG
            NSLog(@"地理位置服务不可用");
    #endif
            if (self.locationCallbackBlock) {
                self.locationCallbackBlock(NO, kCLLocationCoordinate2DInvalid, nil);
            }
            if (self.locationWithLocationCallBackBlock) {
                self.locationWithLocationCallBackBlock(NO, nil, nil);
            }
            
        }
        
    }
    
    
    /**
     是否可以使用定位
     
     即授权未拿到或者未定义plist文件里的授权描述
     info.plist里需要定义的两个key-value信息如下:
     <key>NSLocationAlwaysUsageDescription</key>
     <string>我们将使用你的位置为你提供就近咨询和信息服务</string>
     <key>NSLocationWhenInUseUsageDescription</key>
     <string>我们将使用你的位置为你提供就近咨询和信息服务</string>
     @return YES表示可以使用,NO表示不可以使用,
     */
    - (BOOL)canUseLocation {
        if (![CLLocationManager locationServicesEnabled]) {
            return NO;
        }
        if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways &&
            [CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
            return NO;
        }
        return YES;
    }
    
    
    #pragma mark- CLLocationManagerDelegate
    /**
     授权状态变更代理回调
    
     @param manager 位置管理器
     @param status 状态值
     */
    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
        NSString *message = nil;
        switch (status) {
            case kCLAuthorizationStatusAuthorizedAlways:
                [self startUpdateLocation];
                message = @"一直使用地理位置";
                break;
            case kCLAuthorizationStatusAuthorizedWhenInUse:
                [self startUpdateLocation];
                message = @"使用期间使用地理位置";
                break;
            case kCLAuthorizationStatusDenied:
                message = @"用户禁用地理位置访问服务";
                break;
            case kCLAuthorizationStatusRestricted:
                message = @"系统定位服务功能被限制";
                break;
            case kCLAuthorizationStatusNotDetermined:
                message = @"用户未决定地理位置的使用";
                break;
            default:
                break;
        }
    #ifdef DEBUG
        NSLog(@"地理位置授权状态变化:%@", message);
    #endif
        
    }
    /*
     获取到了定位数据
     */
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
        _location = [locations lastObject];
    #ifdef DEBUG
        NSLog(@"经纬度可能会获取多次");
        NSLog(@"纬度 = %f", _location.coordinate.latitude);
        NSLog(@"经度  = %f", _location.coordinate.longitude);
    #endif
        [self.locationManager stopUpdatingLocation];
        if (self.locationCallbackBlock) {
            self.locationCallbackBlock(YES, _location.coordinate, nil);
        }
        
        if (self.locationWithLocationCallBackBlock) {
            self.locationWithLocationCallBackBlock(YES, _location, nil);
        }
    }
    
    /*
     *定位失败
     */
    - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    #ifdef DEBUG
        NSLog(@"定位失败 error: %@", [error description]);
    #endif
        if (self.locationCallbackBlock) {
            self.locationCallbackBlock(YES, kCLLocationCoordinate2DInvalid, error);
        }
        if (self.locationWithLocationCallBackBlock) {
            self.locationWithLocationCallBackBlock(YES, nil, error);
        }
    }
    
    #pragma mark- getter
    - (CLLocationManager *)locationManager {
        if (_locationManager == nil) {
            _locationManager = CLLocationManager.new;
            _locationManager.delegate = self;
            ///期望的精准度。有五个选项值。我们使用的是最好精度
            _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
            ///刷新距离
            _locationManager.distanceFilter = 10;
        }
        return _locationManager;
    }
    
    - (void)setLocationCallbackBlock:(LocationCallBackWithCoordinateBlock) locationCallBack {
        _locationCallbackBlock = locationCallBack;
    }
    
    - (void)setLocationWithLocationCallBackBlock:(LocationCallBackWithLocationBlock) locationCallBack {
        _locationWithLocationCallBackBlock = locationCallBack;
    }
    
    @end
    
    
    

    解释说明

    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status这个方法里能获取到用户对地理位置信息的授权状态。但是授权过程并不会中断[self.locationManager startUpdatingLocation];启动定位的执行。因为处于授权过程中,所以定位没有结果。导致第一次授权启动定位失败,下次定位没问题。为了解决这个问题。我们在授权状态变成可用状态才进行定位的。即这段代码

    授权后定位.png

    另外再次获取定位信息时只需要[self.locationManager startUpdatingLocation];或者[self startUpdateLocation];就可以了。

    更新时间2018-11-27

    相关文章

      网友评论

          本文标题:iOS CLLocationManager 地理定位

          本文链接:https://www.haomeiwen.com/subject/ktngqqtx.html