Core Location實現系統自帶定位問題

来源:http://www.cnblogs.com/sy-scholar/archive/2017/02/19/6416547.html
-Advertisement-
Play Games

Core Location是iOS SDK中一個提供設備位置的框架。可以使用三種技術來獲取位置:GPS、蜂窩或WiFi。在這些技術中,GPS最為精準,如果有GPS硬體,Core Location將優先使用它。如果設備沒有GPS硬體(如WiFi iPad)或使用GPS獲取當前位置時失敗,Core Lo ...


      Core Location是iOS SDK中一個提供設備位置的框架。可以使用三種技術來獲取位置:GPS、蜂窩或WiFi。在這些技術中,GPS最為精準,如果有GPS硬體,Core Location將優先使用它。如果設備沒有GPS硬體(如WiFi iPad)或使用GPS獲取當前位置時失敗,Core Location將退而求其次,選擇使用蜂窩或WiFi。

      Core Location的大多數功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器來指定位置更新的頻率和精度,以及開始和停止接收這些更新。

要使用位置管理器,必須首先將框架Core Location加入到項目中,再導入其介面文件:

#import <CoreLocation/CoreLocation.h>

並初始化位置管理器,指定更新代理,以及一些更新設置,然後更新

CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager startUpdatingLocation];

 位置管理器委托(CLLocationManagerDelegate)有兩個與位置相關的方法:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
 CLLocation *curLocation = [locations lastObject];
    
    if(curLocation.horizontalAccuracy > 0)
    {
        NSLog(@"當前位置:%.0f,%.0f +/- %.0f meters",curLocation.coordinate.longitude,
              curLocation.coordinate.latitude,
              curLocation.horizontalAccuracy);
    }
    
    if(curLocation.verticalAccuracy > 0)
    {
        NSLog(@"當前海拔高度:%.0f +/- %.0f meters",curLocation.altitude,curLocation.verticalAccuracy);
    }
} - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { //此方法為定位失敗的時候調用。並且由於會在失敗以後重新定位,所以必須在末尾停止更新
  if(error.code == kCLErrorLocationUnknown)
    {
        NSLog(@"Currently unable to retrieve location.");
    }
    else if(error.code == kCLErrorNetwork)
    {
        NSLog(@"Network used to retrieve location is unavailable.");
    }
    else if(error.code == kCLErrorDenied)
    {
        NSLog(@"Permission to retrieve location is denied.");
        [manager stopUpdatingLocation];
    }
}

 

第一個方法處理定位成功,manager參數表示位置管理器實例;locations為一個數組,是位置變化的集合,它按照時間變化的順序存放。如果想獲得設備的當前位置,只需要訪問數組的最後一個元素即可。集合中每個對象類型是CLLocation,它包含以下屬性:

coordinate — 坐標。一個封裝了經度和緯度的結構體。

altitude — 海拔高度。正數表示在海平面之上,而負數表示在海平面之下。

horizontalAccuracy — 位置的精度(半徑)。位置精度通過一個圓表示,實際位置可能位於這個圓內的任何地方。這個圓是由coordinate(坐標)和horizontalAccuracy(半徑)共同決定的,horizontalAccuracy的值越大,那麼定義的圓就越大,因此位置精度就越低。如果horizontalAccuracy的值為負,則表明coordinate的值無效。

verticalAccuracy — 海拔高度的精度。為正值表示海拔高度的誤差為對應的米數;為負表示altitude(海拔高度)的值無效。

speed — 速度。該屬性是通過比較當前位置和前一個位置,並比較它們之間的時間差異和距離計算得到的。鑒於Core Location更新的頻率,speed屬性的值不是非常精確,除非移動速度變化很小。

 

應用程式開始跟蹤用戶的位置時,將在屏幕上顯示一個是否允許定位的提示框。如果用戶禁用定位服務,iOS不會禁止應用程式運行,但位置管理器將生成錯誤。

第二個方法處理這種定位失敗,該方法的參數指出了失敗的原因。如果用戶禁止應用程式定位,error參數將為kCLErrorDenied;如果Core Location經過努力後無法確認位置,error參數將為kCLErrorLocationUnknown;如果沒有可供獲取位置的源,error參數將為kCLErrorNetwork。

通常,Core Location將在發生錯誤後繼續嘗試確定位置,但如果是用戶禁止定位,它就不會這樣做;在這種情況下,應使用方法stopUpdatingLocation停止位置管理器。

 

可根據實際情況來指定位置精度。例如,對於只需確定用戶在哪個國家的應用程式,沒有必要要求Core Location的精度為10米。要指定精度,可在啟動位置更新前設置位置管理器的desiredAccuracy。有6個表示不同精度的枚舉值:

extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation;
extern const CLLocationAccuracy kCLLocationAccuracyBest;
extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;
extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;
extern const CLLocationAccuracy kCLLocationAccuracyKilometer;
extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;

 對位置管理器啟動更新後,更新將不斷傳遞給位置管理器委托,直到停止更新。您無法直接控制這些更新的頻率,但可使用位置管理器的屬性distanceFilter進行間接控制。在啟動更新前設置屬性distanceFilter,它指定設備(水平或垂直)移動多少米後才將另一個更新發送給委托。下麵的代碼使用適合跟蹤長途跋涉者的設置啟動位置管理器:

CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度百米以內
locManager.distanceFilter = 200;//水平或者垂直移動200米調用代理更新位置
[locManager startUpdatingLocation];//啟動位置更新

P.s. 定位要求的精度越高、屬性distanceFilter的值越小,應用程式的耗電量就越大。

位置管理器有一個headingAvailable屬性,它指出設備是否裝備了磁性指南針。如果該屬性為YES,就可以使用Core Location來獲取航向(heading)信息。接收航向更新與接收位置更新極其相似,要開始接收航向更新,可指定位置管理器委托,設置屬性headingFilter以指定要以什麼樣的頻率(以航向變化的度數度量)接收更新,並對位置管理器調用方法startUpdatingHeading:

位置管理器委托協議定義了用於接收航向更新的方法。該協議有兩個與航向相關的方法:

- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
    return YES;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    
}

第一個方法指定位置管理器是否向用戶顯示校準提示。該提示將自動旋轉設備360°。由於指南針總是自我校準,因此這種提示僅在指南針讀數劇烈波動時才有幫助。當設置為YES後,提示可能會分散用戶的註意力,或影響用戶的當前操作。

第二個方法的參數newHeading是一個CLHeading對象。CLHeading通過一組屬性來提供航向讀數:magneticHeading和trueHeading。這些值的單位為度,類型為CLLocationDirection,即雙精度浮點數。這意味著:

如果航向為0.0,則前進方向為北;

如果航向為90.0,則前進方向為東;

如果航向為180.0,則前進方向為南;

如果航向為270.0,則前進方向為西。

CLHeading對象還包含屬性headingAccuracy(精度)、timestamp(讀數的測量時間)和description(這種描述更適合寫入日誌而不是顯示給用戶)。下麵演示了利用這個方法處理航向更新:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    if(newHeading.headingAccuracy >=0)
    {
        NSString *headingDesc = [NSString stringWithFormat:@"%.0f degrees (true), %.0f degrees (magnetic)",newHeading.trueHeading,newHeading.magneticHeading];
        
        NSLog(@"%@",headingDesc);
    }
}

 

trueHeading和magneticHeading分別表示真實航向和磁性航向。如果位置服務被關閉了,GPS和wifi就只能獲取magneticHeading(磁場航向)。只有打開位置服務,才能獲取trueHeading(真實航向)。

下麵的代碼演示了,當存在一個確定了經緯度的地點,當前位置離這個地點的距離及正確航向:

#import "ViewController.h"

#define kDestLongitude 113.12 //精度
#define kDestLatitude 22.23 //緯度
#define kRad2Deg 57.2957795 // 180/π
#define kDeg2Rad 0.0174532925 // π/180

@interface ViewController ()
@property (strong, nonatomic) IBOutlet UILabel *lblMessage;
@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *recentLocation;

-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    self.locationManager.distanceFilter = 1609; //1英里≈1609米
    [self.locationManager startUpdatingLocation];
    
    if([CLLocationManager headingAvailable])
    {
        self.locationManager.headingFilter = 10; //10°
        [self.locationManager startUpdatingHeading];
    }
}

/*
 * According to Movable Type Scripts
 * http://mathforum.org/library/drmath/view/55417.html
 *
 *  Javascript:
 *
 * var y = Math.sin(dLon) * Math.cos(lat2);
 * var x = Math.cos(lat1)*Math.sin(lat2) -
 * Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
 * var brng = Math.atan2(y, x).toDeg();
 */
-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current
{
    double lat1 = current.latitude*kDeg2Rad;
    double lat2 = desired.latitude*kDeg2Rad;
    double lon1 = current.longitude;
    double lon2 = desired.longitude;
    double dlon = (lon2-lon1)*kDeg2Rad;
    
    double y = sin(dlon)*cos(lat2);
    double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dlon);
    
    double heading=atan2(y,x);
    heading=heading*kRad2Deg;
    heading=heading+360.0;
    heading=fmod(heading,360.0);
    return heading;
}

//處理航向 
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    if(self.recentLocation!=nil && newHeading.headingAccuracy>=0)
    {
        CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
        
        double course = [self headingToLocation:destLocation.coordinate current:self.recentLocation.coordinate];
        
        double delta = newHeading.trueHeading - course;
        
        if (abs(delta) <= 10)
        {
            self.imgView.image = [UIImage imageNamed:@"up_arrow.png"];
        }
        else
        {
            if (delta > 180)
            {
                self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
            }
            else if (delta > 0)
            {
                self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
            }
            else if (delta > -180)
            {
                self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
            }
            else
            {
                self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
            }
        }
        self.imgView.hidden = NO;
    }
    else
    {
        self.imgView.hidden = YES;
    }
}

//處理定位成功
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *curLocation = [locations lastObject];
    
    if(curLocation.horizontalAccuracy >= 0)
    {
        self.recentLocation = curLocation;
        
        CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
        
        CLLocationDistance distance = [destLocation distanceFromLocation:curLocation];
        
        if(distance<500)
        {
            [self.locationManager stopUpdatingLocation];
            [self.locationManager stopUpdatingHeading];
            self.lblMessage.text = @"您已經到達目的地!";
        }
        else
        {
            self.lblMessage.text = [NSString stringWithFormat:@"距離目的地還有%f米",distance];
        }
    }
}

//處理定位失敗
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    if(error.code == kCLErrorLocationUnknown)
    {
        NSLog(@"Currently unable to retrieve location.");
    }
    else if(error.code == kCLErrorNetwork)
    {
        NSLog(@"Network used to retrieve location is unavailable.");
    }
    else if(error.code == kCLErrorDenied)
    {
        NSLog(@"Permission to retrieve location is denied.");
        [self.locationManager stopUpdatingLocation];
        self.locationManager = nil;
    }
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 導語:之前做習慣了Framework層的開發,今天在武漢鬥魚公司面試APP客戶端的開發,其中一道題是講述Asynctask的優缺點,我靠,我只是知道有這麼一個東西,會用而已,看來之前的生活太過於安逸,這麼基礎的東西都不會,特意回家總結下。 轉載請註明原文鏈接:http://www.cnblogs.c ...
  • 寫項目的時候就遇到了這個尷尬的問題,有的是需要進行網路請求的,或者有的手機有點卡頓,用戶就會連續點擊,經常出現的就是接連Push兩個同樣的控制器,當然還有重覆調用網路請求的方法,等等等等。。。。 比較笨的方法就是在每個點擊的地方把用戶交互給關掉,等到功能實現後之後再將這個按鈕的用戶交互恢復。 當然了 ...
  • 很少用DatePicker,項目需要用到DatePicker,再用時發現跟以前有些變化,重新簡單梳理一下DatePicker的用法。 先上效果圖: 首先是xml文件的用法: 以前是用android:spinnersShown和android:calendarViewShown這兩個屬性來控制Date ...
  • 現在做的項目遇到一個問題,設計給過來的圖片只有一種狀態,但是實現的需求是要求有兩個狀態,另一種選狀態為圖片背景加邊框。如圖: 剛開使用使用ImageView ,ImageViewButton 效果不是很明顯; 後來發現 layer-list 能很好的實現這個效果,先分別建 正常模式與選中模式的xml ...
  • 1.概述 在之前的博文中簡單介紹過如何實現fragment之間的信息交互:《Android中Fragment與Activity之間的交互(兩種實現方式)》,今天繼續給大家介紹一種可以實現此效果的另外一種方式EventBus。(相比於handler,介面回調,bundle傳參,這個簡單好用到哭) Ev ...
  • 轉自http://blog.csdn.net/wangyanchang21/article/details/50932191 在很多時候都會遇到錯誤, 還會帶有一些 Error Code , 比如在各種網路請求, 文件操作, cookie 錯誤等各種錯誤的代碼和錯誤代碼對應的含義. 以下目錄為 SO ...
  • 註意:刪除的時候要謹慎!別什麼圖都刪了,看看對項目有沒有作用。這個插件有時也會有一定的誤差。 具體操作步驟: 1.去github上下載LSUnusedResources(下載地址:https://github.com/tinymind/LSUnusedResources/) 2.運行LSUnused ...
  • 本篇博客就來介紹一下iOS App中主題切換的常規做法,當然本篇博客中只是提到了一種主題切換的方法,當然還有其他方法,在此就不做過多贅述了。本篇博客中所涉及的Demo完全使用Swift3.0編寫完成,並使用iOS的NSNotification來觸發主題切換的動作。本篇博客我們先對我們的主題系統進行設 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...