【iOS乾貨】☞ 定位

1、簡介

  定位是一個很經常使用的功能,如一些地圖軟件打開以後 若是用戶容許軟件定位的話,那麼打開軟件後就會自動鎖定到當前位置,若是用戶手機移動那麼當前位置也會跟隨着變化。要實現這個功能須要使用Core Loaction中CLLocationManager類。git

1. 定位的實際應用場景:

  • 導航:去任意陌生的地方
  • 周邊:找餐館、找酒店、找銀行、找電影院等

2. 涉及技術:

  • Core Location 框架用於地理定位。(單純的定位,不須要顯示地圖)

經常使用類:以CL前綴開頭web

CLLocation:(結構體類型)經緯度數組

CLLocationManager:  定位管理類,位置管理器,全局惟一存在,作定位用。 微信

CLLocationManagerDelegate:  監聽用戶是否願意定位(iOS8後要問),監聽用戶的位置(經緯度)框架

  • Map Kit 框架用於地圖展現。(和地圖結合的定位,如:百度地圖、高德地圖等)

經常使用類:以MK前綴開頭優化

MKMapView: 顯示地圖視圖編碼

MKMapViewDelegate: 地圖視圖的協議(定位;地圖視圖移動;定位用戶的位置)atom

3. 專業術語:

  • LBS:Location Based Service,基於位置的服務。(如,打車:基於位置提供了叫車服務。)
  • SoLoMo:Social Local Mobile(索羅門),社交本地移動。(如,陌陌、微信、QQ)

4. iOS定位的方式:(按定位準確性排名)

  • GPS(Global Positioning System,全球衛星定位系統)定位
  • 移動基站/蜂窩/流量
  • wifi定位

2、用戶隱私的保護

1. 權限設置說明

  從iOS 6開始,蘋果在保護用戶隱私方面作了很大的增強,如下操做都必須通過用戶批准受權spa

    (1)要想得到用戶的位置3d

    (2)想訪問用戶的通信錄、日曆、相機、相冊等

    當想訪問用戶的隱私信息時,系統會自動彈出一個對話框讓用戶受權

  

  注意:一旦用戶選擇了「Don’t Allow」,意味着你的應用之後就沒法使用定位功能,且當用戶第一次選擇了以後,之後就不再會提醒進行設置。

  所以在程序中應該進行判斷,若是發現本身的定位服務沒有打開,那麼應該提醒用戶打開定位服務功能。

  CLLocationManager有個類方法能夠判斷當前應用的定位功能是否可用+ (BOOL)locationServicesEnabled;

  經常使用的方法:截圖告訴用戶,應該怎麼打開受權

2. 請求用戶受權

  配置徵求用戶是否贊成受權的彈出框:(在Info.plist裏配置,添加相應的key 才能彈出受權框)

  • 始終受權:

    

   

  • 使用應用期間受權:

  

  

  • 修改plist配置,添加Key:

   

  

3. 自定義模擬器的位置

  自定義一個位置模擬位置,寫哪兒模擬器就認爲你在哪兒(定位就定在這兒)

   

2、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.建立位置管理器(定位用戶的位置)
        _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

 注意:若是發現本身的定位服務沒有打開,那麼應該提醒用戶打開定位服務功能。定位服務是比較耗電的,若是是作定位服務(不必實時更新的話),那麼定位了用戶位置後,應該中止更新位置。

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);

3、地理編碼/反地理編碼

1. 簡單說明:

    Geography(地理) + code(編碼),CLGeocoder:地理編碼器,其中Geo是地理英文單詞Geography的簡寫。

  • 地理編碼將地名 轉換成 經緯度的過程。給定一個地址名(城市/街道/省名字) —> 返回地址名所在的位置(經緯度)
  • 反地理編碼 (使用頻率高):將經緯度 轉換成 地名的過程。給定一個經緯度 —> 返回該經緯度的詳細信息(國家/省/城市/街道/店鋪)

2. CLGeocoder 的使用:

    使用 CLGeocoder 能夠完成「地理編碼」和「反地理編碼」

    

3. CLPlacemark 類經常使用屬性:

@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

 4. 完整代碼:

// 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"]); } }];

4、MapKit框架的使用(和地圖結合的定位)

1. 導入框架:MapKit.framework

2. 導入頭文件:#import <MapKit/MapKit.h>

3. 經緯度:

 

4. MapView的使用:

  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

 

5. 回到用戶的位置(中心點回到原來的位置、跨度和原來保持一致)

  

  

相關文章
相關標籤/搜索