#import "ViewController.h" #import <CoreLocation/CoreLocation.h> @interface ViewController ()<CLLocationManagerDelegate> /** 定位管理者 */ @property (nonatomic, strong) CLLocationManager *locationManger; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 開始定位 [self.locationManger startUpdatingLocation]; /* kCLLocationAccuracyBestForNavigation kCLLocationAccuracyBest; kCLLocationAccuracyNearestTenMeters; kCLLocationAccuracyHundredMeters; kCLLocationAccuracyKilometer; kCLLocationAccuracyThreeKilometers; */ // 單次定位請求 // 按照精確度從低到高, 逐個進行定位 // 若是在有效時間內, 定位到, 精確度最高的位置, 直接經過代理告訴外界 // 若是當前尚未定位到精確度最高的位置, 可是已經超時, 這時, 就會把當前精確度對應的位置信息, 經過代理告訴外界 // 若是定位失敗, 就會調用定位失敗的代理方法 // 不能與 startUpdatingLocation 一塊兒使用 // if(isIOS(9.0)) // { // // [self.locationM requestLocation]; // } } #pragma mark - CLLocationManagerDelegate // 定位成功時調用 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { CLLocation *l = [locations lastObject]; NSLog(@"%@",l.description); } // 定位失敗時調用 -(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"定位失敗"); } - (CLLocationManager *)locationManger{ if (!_locationManger) { _locationManger = [[CLLocationManager alloc] init]; _locationManger.delegate = self; // If the NSLocationAlwaysUsageDescription key is not specified in your // Info.plist, this method will do nothing, as your app will be assumed not // to support Always authorization. [_locationManger requestAlwaysAuthorization]; } return _locationManger; } @end
打印輸出ios
<+37.32462634,-122.02381665> +/- 5.00m (speed 3.51 mps / course 267.11) @ 16/5/10 中國標準時間 上午8:42:58 ... ...
[_locationManger requestAlwaysAuthorization]
若是你的info.plist 中沒有配置相關的key。這行代碼什麼都不會作的。git
#define isIOS(version) ([[UIDevice currentDevice].systemVersion floatValue] >= version)
/** 位置管理者 */ - (CLLocationManager *)locationM { if (!_locationM) { _locationM = [[CLLocationManager alloc] init]; _locationM.delegate = self; /*----------ios8.0+定位適配-----------*/ if(isIOS(8.0)) { // 請求前臺定位受權 // 默認狀況下, 只能在前臺獲取用戶位置信息 // 若是想要在後臺獲取用戶位置, 必須勾選後臺模式 location updates, 可是 , 相比於iOS8.0-, 會出現一個藍條不斷髮提醒用戶 [_locationM requestWhenInUseAuthorization]; /*----------ios9.0+定位適配-----------*/ // 若是在iOS9.0 , 當前處前臺定位受權狀態下, 那麼即便勾選了後臺模式 location updates, 也不會 獲取用戶位置, 除非,設置一下屬性爲YES; // _locationM.allowsBackgroundLocationUpdates = YES; if (isIOS(9.0)) { _locationM.allowsBackgroundLocationUpdates = YES; // self.test = [_locationM allowsBackgroundLocationUpdates]; } // [_locationM requestAlwaysAuthorization]; // 請求先後臺定位受權 // 默認狀況下, 不管是在前臺仍是後臺, 均可以獲取用戶位置信息, 並且不會出現藍條 // 跟是否勾選後臺模式location updates沒有關係 // 若是當前的用戶受權狀態 != 用戶未決定狀態, 那麼這個方法不會有效 } // 適配方案2 // if ([_locationM respondsToSelector:@selector(requestAlwaysAuthorization)]) // { // [_locationM requestAlwaysAuthorization]; // } } return _locationM; }
定位代理,當前定位受權狀態發生改變時調用數組
/** * 當前定位受權狀態發生改變時調用 * * @param manager 位置管理者 * @param status 狀態 */ - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{ switch (status) { case kCLAuthorizationStatusNotDetermined: { NSLog(@"用戶未決定"); break; } case kCLAuthorizationStatusDenied: { // 判斷當前設備是否支持定位, 定位服務是否開啓 if([CLLocationManager locationServicesEnabled]) { NSLog(@"真正被拒絕"); // 提醒給APP 受權 // iOS8.0- , 截圖 // iOS8.0+ , 經過調用一個方法, 來直接到達設置界面 // 跳轉核心代碼 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL: url]; } } else { NSLog(@"定位服務被關閉"); } break; } // 系統預留字段 case kCLAuthorizationStatusRestricted: { NSLog(@"受限制"); break; } case kCLAuthorizationStatusAuthorizedAlways: { NSLog(@"先後臺定位受權"); break; } case kCLAuthorizationStatusAuthorizedWhenInUse: { NSLog(@"前臺定位受權"); break; } default: break; } }
小插曲app
// 提醒給APP 受權 // iOS8.0- , 截圖 // iOS8.0+ , 經過調用一個方法, 來直接到達設置界面 // 跳轉核心代碼 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL: url]; }
/** * 當定位到以後, 進入這個方法 * * @param manager 位置管理者 * @param locations 位置數組 * id + 泛型 : 數組裏面的對象 與這個對象的關係 is kind of */ -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { // 按時間排序, 若是想要拿到最新的位置, 直接拿最後一個 CLLocation *location = [locations lastObject]; /** coordinate : 經緯度 CLLocationCoordinate2D altitude : 海拔 horizontalAccuracy : 若是是負值, 表明當前位置數據不可用 verticalAccuracy : 海拔 -- 若是是負值, 表明當前海拔數據不可用 course : 航向 (0.0----359.9) speed : 速度 floor : 樓層 方法: distanceFromLocation : 計算兩個座標之間的物理直線距離 */ // NSLog(@"%@", location); // >場景演示:打印當前用戶的行走方向,偏離角度以及對應的行走距離, // 例如:」北偏東 30度 方向,移動了 8米」 if (location.horizontalAccuracy < 0) { return; } // 1. 肯定當前航向(北偏東) NSInteger index = (int)location.course / 90; NSArray *courseStrArray = @[@"北偏東", @"東偏南", @"南偏西", @"西偏北"]; NSString *courseStr = courseStrArray[index]; // 2. 肯定偏離角度 NSInteger angle = (int)location.course % 90; // 表明是正方向 if (angle == 0) { courseStr = [@"正" stringByAppendingString:[courseStr substringToIndex:1]]; } // 3. 肯定行走距離 CGFloat distance = 0; if (_lastLoc) { distance = [location distanceFromLocation:_lastLoc]; } _lastLoc = location; // 4. 拼串打印 // 例如:」北偏東 30度 方向,移動了 8米」 NSString *noticeStr; if(angle != 0) { noticeStr = [NSString stringWithFormat:@"%@ %zd 度方向, 移動了 %f米", courseStr, angle, distance]; }else { noticeStr = [NSString stringWithFormat:@"%@ 方向, 移動了 %f米", courseStr, distance]; } NSLog(@"%@", noticeStr); }
// 1. 獲取當前設備朝向("磁力計傳感器") [self.locationM startUpdatingHeading];
當前獲取到設備朝向(CLLocationManagerDelegate)框架
/** * 當前獲取到設備朝向時, 調用 * * @param manager 位置管理者 * @param newHeading 當前設備朝向對象 */ -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { /** * CLHeading * magneticHeading : 距離磁北方向的角度 * trueHeading : 距離真北方向的角度 * headingAccuracy : 若是這個值是負數, 那麼表明角度不可用 */ if(newHeading.headingAccuracy < 0) return; // 1. 獲取設備朝向(角度) CLLocationDirection angle = newHeading.magneticHeading; // 1.1 把角度 轉換成弧度 float radius = angle / 180.0 * M_PI; // 2. 反方向旋轉指南針圖片(弧度) [UIView animateWithDuration:0.5 animations:^{ self.compassView.transform = CGAffineTransformMakeRotation(-radius); }]; }
// -1. 判斷當前設備是否支持區域監聽(區域類型) if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) { return; }
// 0. 建立區域中心 CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.234); // 0.1 建立區域半徑 CLLocationDistance distance = 1000.0; if(distance > self.locationM.maximumRegionMonitoringDistance) { distance = self.locationM.maximumRegionMonitoringDistance; } // 1. 建立一個區域 CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:distance identifier:@"小碼哥"]; // 2. 監聽一個區域(只有用戶有進入區域, 或者離開區域動做的時候 纔會經過代理告訴外界) [self.locationM startMonitoringForRegion:region]; // 請求某個區域的狀態 // 不止能夠獲取到指定區域的狀態, 並且當狀態發生變化時, 也會調用對應的代理方法, 告訴咱們 [self.locationM requestStateForRegion:region];
代理回調 #pragma mark - CLLocationManagerDelegateide
#pragma mark - CLLocationManagerDelegate /** * 進入指定區域時調用 * * @param manager 位置管理者 * @param region 區域 */ -(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { NSLog(@"進入區域---%@", region.identifier); self.noticeLabel.text = @"小碼哥歡迎你, 給你技術"; } /** * 離開指定區域時調用 * * @param manager 位置管理者 * @param region 區域 */ -(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { NSLog(@"離開區域---%@", region.identifier); self.noticeLabel.text = @"歡迎下次來複讀"; } /** * 當前請求指定區域狀態時, 回調的代理方法 * * @param manager 位置管理者 * @param state 狀態 * @param region 區域 */ -(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region { /** * CLRegionStateUnknown, // 不知道 CLRegionStateInside, // 在區域內部 CLRegionStateOutside // 在區域外部 */ if(state == CLRegionStateInside) { self.noticeLabel.text = @"小碼哥歡迎你, 給你技術"; }else if (state == CLRegionStateOutside) { self.noticeLabel.text = @"歡迎下次來複讀"; } }
/** 地理編碼管理器 */ @property (nonatomic, strong) CLGeocoder *geoC; #pragma mark - 懶加載 /** 地理編碼管理器 */ - (CLGeocoder *)geoC { if (!_geoC) { _geoC = [[CLGeocoder alloc] init]; } return _geoC; }
// 地理編碼(地址關鍵字 ->經緯度 ) - (IBAction)geoCode { NSString *address = self.addressTV.text; // 容錯處理 if([address length] == 0) { return; } // 根據地址關鍵字, 進行地理編碼 [self.geoC geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { /** * CLPlacemark : 地標對象 * location : 對應的位置對象 * name : 地址全稱 * locality : 城市 * 按相關性進行排序 */ CLPlacemark *pl = [placemarks firstObject]; if(error == nil) { NSLog(@"%f----%f", pl.location.coordinate.latitude, pl.location.coordinate.longitude); NSLog(@"%@", pl.name); self.addressTV.text = pl.name; self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue; self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue; } }]; }
// 反地理編碼(把經緯度---> 詳細地址) - (IBAction)reverseGeoCode { double latitude = [self.latitudeTF.text doubleValue]; double longitude = [self.longitudeTF.text doubleValue]; CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark *pl = [placemarks firstObject]; if(error == nil) { NSLog(@"%f----%f", pl.location.coordinate.latitude, pl.location.coordinate.longitude); NSLog(@"%@", pl.name); self.addressTV.text = pl.name; self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue; self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue; } }]; }
// // XMGLocationTool.h // 09-代理模式到block模式的轉換 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import <Foundation/Foundation.h> #import <CoreLocation/CoreLocation.h> #import "Singleton.h" typedef void(^ResultBlock)(CLLocation *location, CLPlacemark *placeMark, NSString *errorMsg); @interface XMGLocationTool : NSObject single_interface(XMGLocationTool) /** * 直接獲取當前位置信息, 而後經過代碼塊告訴外界 * * @param block 代碼塊 */ - (void)getCurrentLocation:(ResultBlock)block; @end
// // XMGLocationTool.m // 09-代理模式到block模式的轉換 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import "XMGLocationTool.h" #import <UIKit/UIKit.h> #define isIOS(version) ([[UIDevice currentDevice].systemVersion floatValue] >= version) @interface XMGLocationTool()<CLLocationManagerDelegate> /** 記錄要執行的代碼塊 */ @property (nonatomic, copy) ResultBlock block; /** 位置管理者 */ @property (nonatomic, strong) CLLocationManager *locationM; /** 地理編碼管理器 */ @property (nonatomic, strong) CLGeocoder *geoC; @end @implementation XMGLocationTool #pragma mark - 懶加載 /** 位置管理者 */ - (CLLocationManager *)locationM { if (!_locationM) { _locationM = [[CLLocationManager alloc] init]; _locationM.delegate = self; // ios8.0+須要請求受權 if (isIOS(8.0)) { // 要在此處, 請求受權, 可是請求哪一個權限, 無法肯定, 靠其餘開發者肯定; // 1. 獲取info.plist 文件內容 NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary]; // NSLog(@"%@", infoDic); // 2. 獲取其餘開發人員, 填寫的key NSString *always = infoDic[@"NSLocationAlwaysUsageDescription"]; NSString *whenInUse = infoDic[@"NSLocationWhenInUseUsageDescription"]; if ([always length] > 0) { [_locationM requestAlwaysAuthorization]; } else if ([whenInUse length] > 0) { [_locationM requestWhenInUseAuthorization]; // 在前臺定位受權狀態下, 必須勾選後臺模式location udpates才能獲取用戶位置信息 NSArray *services = infoDic[@"UIBackgroundModes"]; if (![services containsObject:@"location"]) { NSLog(@"友情提示: 當前狀態是前臺定位受權狀態, 若是想要在後臺獲取用戶位置信息, 必須勾選後臺模式 location updates"); } else { if (isIOS(9.0)) { _locationM.allowsBackgroundLocationUpdates = YES; } } }else { NSLog(@"錯誤---若是在iOS8.0以後定位, 必須在info.plist, 配置NSLocationWhenInUseUsageDescription 或者 NSLocationAlwaysUsageDescription"); } } } return _locationM; } /** 地裏編碼管理器 */ - (CLGeocoder *)geoC { if (!_geoC) { _geoC = [[CLGeocoder alloc] init]; } return _geoC; } single_implementation(XMGLocationTool) - (void)getCurrentLocation:(ResultBlock)block { // 先記錄代碼塊, 而後在合適的位置調用這個代碼塊 self.block = block; // 在此處, 並不能直接獲取當前的用戶位置, 還有地標 if ([CLLocationManager locationServicesEnabled]) { [self.locationM startUpdatingLocation]; } else { self.block(nil, nil, @"定位服務沒有開啓"); } } #pragma mark - CLLocationManagerDelegate -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location = [locations lastObject]; if (location.horizontalAccuracy < 0) { return; } // 在這裏, 能夠獲取到位置信息 // 在這裏, 還沒發, 獲取到地標對象, 因此, 在此處, 要進一步進行反地理編碼 [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { if (error == nil) { // 獲取地標對象 CLPlacemark *pl = [placemarks firstObject]; // 在此處, 最適合, 執行存儲的代碼塊 self.block(location, pl, nil); } else { self.block(location, nil, error.localizedDescription); } }]; // 關閉定位服務 [manager stopUpdatingLocation]; } @end
掉用this
// // ViewController.h // 09-代理模式到block模式的轉換 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
// // ViewController.m // 09-代理模式到block模式的轉換 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import "ViewController.h" #import "XMGLocationTool.h" @interface ViewController () @end @implementation ViewController - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [[XMGLocationTool sharedXMGLocationTool] getCurrentLocation:^(CLLocation *location, CLPlacemark *placeMark, NSString *errorMsg) { if([errorMsg length] > 0) { NSLog(@"%@", errorMsg); } else { NSLog(@"%@---%@", placeMark.name, location); } }]; } @end