定位是一個很經常使用的功能,如一些地圖軟件打開以後 若是用戶容許軟件定位的話,那麼打開軟件後就會自動鎖定到當前位置,若是用戶手機移動那麼當前位置也會跟隨着變化。要實現這個功能須要使用Core Loaction中CLLocationManager類。git
經常使用類:以CL前綴開頭web
CLLocation:(結構體類型)經緯度數組
CLLocationManager: 定位管理類,位置管理器,全局惟一存在,作定位用。 微信
CLLocationManagerDelegate: 監聽用戶是否願意定位(iOS8後要問),監聽用戶的位置(經緯度)框架
經常使用類:以MK前綴開頭優化
MKMapView: 顯示地圖視圖編碼
MKMapViewDelegate: 地圖視圖的協議(定位;地圖視圖移動;定位用戶的位置)atom
從iOS 6開始,蘋果在保護用戶隱私方面作了很大的增強,如下操做都必須通過用戶批准受權spa
(1)要想得到用戶的位置3d
(2)想訪問用戶的通信錄、日曆、相機、相冊等
當想訪問用戶的隱私信息時,系統會自動彈出一個對話框讓用戶受權
注意:一旦用戶選擇了「Don’t Allow」,意味着你的應用之後就沒法使用定位功能,且當用戶第一次選擇了以後,之後就不再會提醒進行設置。
所以在程序中應該進行判斷,若是發現本身的定位服務沒有打開,那麼應該提醒用戶打開定位服務功能。
CLLocationManager有個類方法能夠判斷當前應用的定位功能是否可用+ (BOOL)locationServicesEnabled;
經常使用的方法:截圖告訴用戶,應該怎麼打開受權
配置徵求用戶是否贊成受權的彈出框:(在Info.plist裏配置,添加相應的key 才能彈出受權框)
自定義一個位置模擬位置,寫哪兒模擬器就認爲你在哪兒(定位就定在這兒)
#pragma mark - CLLocationManagerDelegate 實現協議中的方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
#import "ViewController.h" #import <CoreLocation/CoreLocation.h> // 導入頭文件 @interface ViewController () <CLLocationManagerDelegate> // 聲明一個屬性幫助咱們來定位,幹什麼都要問他 @property (nonatomic, strong) CLLocationManager *manager; @end @implementation ViewController #pragma mark ---懶加載 -(CLLocationManager *)manager { if (_manager==nil) { // 1.建立位置管理器(定位用戶的位置) _manager=[[CLLocationManager alloc]init]; // 2.設置代理(設置誰來監聽用戶的位置) _manager.delegate=self; } return _manager; } - (void)viewDidLoad { [super viewDidLoad]; // 請求用戶受權 // iOS8以後纔開始徵求用戶贊成,iOS8以前不用徵求贊成直接定位 if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { // 注意:iOS8以後要配置info.plist文件:添加key(NSLocationAlwaysUsageDescription / NSLocationWhenInUseUsageDescription),才能彈出受權框
// 兩種受權:始終(永久)受權、使用應用期間受權。
// 注意:若是同時寫了下面兩種受權,程序打開的時候會出現兩次受權提示。大多數狀況下,咱們根據程序的需求寫一種受權方式就能夠了。 //1) 永久受權。不管當前程序在前臺或後臺都受權/都定位 (key:NSLocationAlwaysUsageDescription) //[self.manager requestAlwaysAuthorization]; //請求老是受權 //2) 當用戶正在使用的時候受權。只有程序在前臺運行的時候纔會受權(徵求用戶是否願意只在前臺定位) (key:NSLocationWhenInUseUsageDescription) [self.manager requestWhenInUseAuthorization]; //請求在使用時的受權(在前臺),大多數APP使用的是這種受權 } else { // 調用開始定位方法。直接定位(不須要徵求用戶贊成) 定位裏面幹什麼都要問manager [self.manager startUpdatingLocation];//開始定位 } } #pragma mark - CLLocationManagerDelegate 實現協議中的方法 //1.查看用戶是否贊成(這個方法監聽用戶有沒有點容許/不容許),用戶贊成了就調用第二個方法 /** * @param status 用戶受權的狀態 (用戶是否贊成) * 經常使用的兩個狀態: * 1) kCLAuthorizationStatusDenied:用戶不一樣意定位 * 用戶不容許自動定位時,能夠手動選擇城市定位(如:墨跡天氣,用戶手動選擇一個城市,把城市的天氣推送給你) * 2) kCLAuthorizationStatusAuthorizedWhenInUse:用戶容許在使用期間(前臺)定位 */ -(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { //判斷兩種經常使用的受權狀態 switch (status) { case kCLAuthorizationStatusAuthorizedWhenInUse: //用戶容許在使用期間(前臺)定位
// 實現持續定位,一般要作一些優化(目的是節省電量和流量)。
// 1. 設置距離篩選器,當用戶位置發生必定改變以後再調用代理方法2(避免調用太頻繁)。
// 當用戶位置發生超過10米的變化,再從新開始定位(即調用代理方法2)
self.manger.distanceFilter = 10; // 2. 設置定位的精準度(常量值:kXXXBest最好的、十米範圍內、百米範圍內、公里範圍內、三公里範圍內) // 咱們能夠下降定位的精準度,實際上下降了與衛星之間的計算,以此節省電量和流量。精確度越高越費電量/流量,通常選十米/百米範圍內爲宜。 self.manager.desiredAccuracy = kCLLocationAccuracyBest;//定位的精確度 [self.manager startUpdatingLocation]; //開始定位操做 break; case kCLAuthorizationStatusDenied: //用戶不容許定位(第二種方案) NSLog(@"用戶不容許定位!"); break; default: break; } } //2.已經定位到用戶的位置會調用這個方法 /** * 當完成位置更新的時候調用。當定位到用戶的位置時,就會調用(調用的頻率比較頻繁)
* 當調用了startUpdatingLocation方法後,就開始不斷地定位用戶的位置,中途會頻繁地調用代理的下面方法 * @param locations 用戶的位置(數組類型,最後一項是用戶最新的位置;數組裏至少有一項) */ -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location=[locations lastObject];//獲取用戶最新的位置 NSLog(@"緯度:%f, 經度:%f", location.coordinate.latitude, location.coordinate.longitude); //打印獲取用戶位置的經緯度 // 中止用戶定位/中止更新位置(不中止,就會一直不停的定位,即持續定位;中止後該代理方法就不會再頻繁調用,即一次定位) [self.manager stopUpdatingLocation]; // Xcode7如下只定位一次(打印經緯度一次),Xcode7以上會定位三次(打印經緯度三次) } @end
注意:若是發現本身的定位服務沒有打開,那麼應該提醒用戶打開定位服務功能。定位服務是比較耗電的,若是是作定位服務(不必實時更新的話),那麼定位了用戶位置後,應該中止更新位置。
// 比較兩個位置之間的距離(如,北京與西安的距離) CLLocation *location1 = [[CLLocation alloc]initWithLatitude:40 longitude:116]; CLLocation *location2 = [[CLLocation alloc]initWithLatitude:34.27 longitude:108.93]; // 比較直線距離 CLLocationDistance distance = [location1 distanceFromLocation:location2]; NSLog(@"北京與西安的直線距離爲:%f公里", distance / 1000);
Geography(地理) + code(編碼),CLGeocoder:地理編碼器,其中Geo是地理英文單詞Geography的簡寫。
使用 CLGeocoder 能夠完成「地理編碼」和「反地理編碼」
@interface CLPlacemark : NSObject //地標類 @property (nonatomic, readonly, copy) CLLocation *location; //地理位置,能夠獲取經緯度 @property (nonatomic, readonly, copy) NSDictionary *addressDictionary; //詳細的地址信息 @property (nonatomic, readonly) CLRegion *region; //區域 @property (nonatomic, readonly) NSString *name; //地址名稱 @property (nonatomic, readonly) NSString *locality; //城市 @end
// 1. 建立一個CLGeocoder對象 CLGeocoder *geocoder = [[CLGeocoder alloc] init]; // 2. 開始地理編碼 /** * 說明:調用下面的方法開始編碼,無論編碼是成功仍是失敗都會調用block中的方法 * 給一個地名,返回一個block回調參數 * @param placemarks 地標數組,主要的是CLLocation / 城市屬性 */ [geocoder geocodeAddressString:@"下沙" completionHandler:^(NSArray *placemarks, NSError *error) { //1)若是有錯誤信息,或者是數組中獲取的地名元素數量爲0,那麼說明沒有找到 if (placemarks.count == 0 || error) { NSLog(@"你輸入的地址沒找到,可能在火星上"); } else { //2)編碼成功,找到了具體的位置信息 /** * 遍歷地表數組: * 這裏數組中有一個/多個相關的位置信息對象(給一個名稱,可能對應多個位置信息) */ for (CLPlacemark *placemark in placemarks) { // 打印查看找到的全部的位置信息 NSLog(@"詳細地址名稱:%@", placemark.name); NSLog(@"經緯度座標:%.4f, %.4f" , placemark.location.coordinate.longitude, placemark.location.coordinate.latitude); } // 取得第一個地標,地標中存儲了詳細的地址信息,注意:一個地名可能搜索出多個地址 CLPlacemark *placemark = [placemarks firstObject]; //1>詳細地址名稱 NSLog(@"詳細地址名稱:%@", placemark.name); //2>經緯度 NSLog(@"經緯度座標:%.4f, %.4f" , placemark.location.coordinate.longitude, placemark.location.coordinate.latitude); } }];
// 1. 設置經緯度 CLLocation *location = [[CLLocation alloc]initWithLatitude:40 longitude:116]; // 2.開始反地理編碼 [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { // 1)若是有錯誤信息,或者是數組中獲取的地名元素數量爲0,那麼說明沒有找到 if (placemarks.count == 0 || error) { NSLog(@"你輸入的地址沒找到,可能在火星上"); } else { //2)編碼成功 //這裏不用for循環遍歷,由於數組中只有惟一的一個對象(經緯度必定,地名也必定),直接取出便可 CLPlacemark *placemark = [placemarks firstObject]; self.reverseDetailAddressLabel.text = placemark.name; NSLog(@"詳細地址名稱:%@", placemark.addressDictionary[@"FormattedAddressLines"]); } }];
MapKit中有一個比較重要的UI控件:MKMapView,專門用於地圖顯示。
#import "ViewController.h" #import <MapKit/MapKit.h> @interface ViewController () <MKMapViewDelegate> @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property (nonatomic, strong) CLLocationManager *manager; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 初始化manager self.manager = [[CLLocationManager alloc]init]; // 請求受權:徵求用戶贊成(假定贊成,在targets-->Info/Info.plist中添加key) if ([self.manager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { [self.manager requestWhenInUseAuthorization]; } //設置地圖視圖的代理 self.mapView.delegate = self; // 設置地圖的屬性 self.mapView.rotateEnabled = NO;//設置不容許旋轉 self.mapView.mapType = MKMapTypeHybrid;//地圖的顯示類型(混合類型,默認是Standard類型) // 設定地圖屬性(開始定位/已經把用戶的位置顯示在地圖上) // 用戶的跟蹤模式,地圖視圖的跟蹤模式是跟隨着用戶的位置而變化,當前地圖必定會顯示用戶的位置 self.mapView.userTrackingMode=MKUserTrackingModeFollow; } #pragma mark - MKMapViewDelegate 協議 // 完成用戶位置更新的時候調用,已經定位到用戶的位置調用這個方法 -(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { // 已經定位到用戶的位置 NSLog(@"location:%f, %f",userLocation.location.coordinate.latitude, userLocation.location.coordinate.longitude);
userLocation.title = @"用戶位置-標題";
userLocation.subtitle = @"用戶位置-子標題";
// 這裏能夠經過反地理編碼把經緯度轉成地名,用戶點擊時就能夠顯示用戶位置的提示。 } @end