一、簡介 1. 定位的實際應用場景: 導航:去任意陌生的地方 周邊:找餐館、找酒店、找銀行、找電影院等 2. 涉及技術: Core Location 框架:用於地理定位。(單純的定位,不需要顯示地圖) 常用類:以CL首碼開頭 CLLocation:(結構體類型)經緯度 CLLocationManag ...
一、簡介
1. 定位的實際應用場景:
- 導航:去任意陌生的地方
- 周邊:找餐館、找酒店、找銀行、找電影院等
2. 涉及技術:
- Core Location 框架:用於地理定位。(單純的定位,不需要顯示地圖)
常用類:以CL首碼開頭
CLLocation:(結構體類型)經緯度
CLLocationManager: 定位管理類,位置管理器,全局唯一存在,做定位用。
CLLocationManagerDelegate: 監聽用戶是否願意定位(iOS8後要問),監聽用戶的位置(經緯度)
- Map Kit 框架:用於地圖展示。(和地圖結合的定位,如:百度地圖、高德地圖等)
常用類:以MK首碼開頭
MKMapView: 顯示地圖視圖
MKMapViewDelegate: 地圖視圖的協議(定位;地圖視圖移動;定位用戶的位置)
3. 專業術語:
- LBS:Location Based Service,基於位置的服務。(如,打車:基於位置提供了叫車服務。)
- SoLoMo:Social Local Mobile(索羅門),社交本地移動。(如,陌陌、微信、QQ)
4. iOS定位的方式:(按定位準確性排名)
- GPS(Global Positioning System,全球衛星定位系統)定位
- 移動基站/蜂窩/流量
- wifi定位
二、CoreLocation框架的使用
1. 導入框架:CoreLocation.framework
2. 導入頭文件:#import <CoreLocation/CoreLocation.h>
3. CLLocationManager的常用操作:
- 開始用戶定位:- (void)startUpdatingLocation;
- 停止用戶定位:- (void)stopUpdatingLocation;
- 當調用了startUpdatingLocation方法後,就開始不斷地定位用戶的位置,中途會頻繁地調用代理的下麵方法:
#pragma mark - CLLocationManagerDelegate 實現協議中的方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
4. 定位用戶的位置:
#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.創建位置管理器(定位用戶的位置) self.manager=[[CLLocationManager alloc]init]; // 2.設置代理(設置誰來監聽用戶的位置) self.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.已經定位到用戶的位置會調用這個方法 /** * 當完成位置更新的時候調用。當定位到用戶的位置時,就會調用(調用的頻率比較頻繁) * @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
5. 計算兩點之間的直線距離
// 比較兩個位置之間的距離(如,北京與西安的距離) 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);
三、地理編碼/反地理編碼
- 地理編碼:將地名 轉換成 經緯度的過程。
給定一個地址名(城市/街道/省名字) —> CLGeocoder —> 返回地址名所在的位置(經緯度)
- 反地理編碼 (使用頻率高):將經緯度 轉換成 地名的過程。
給定一個經緯度 —> CLGeocoder —> 返回該經緯度的詳細信息(國家/省/城市/街道/店鋪)
// 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"]); } }];