IOS CoreLocation实现系统自带定位的方法

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS开发中使用CoreLocation框架处理地理编码的方法

    一.简介 1.在移动互联网时代,移动app能解决用户的很多生活琐事,比如 (1)导航:去任意陌生的地方 (2)周边:找餐馆.找酒店.找银行.找电影院 2.在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这2大功能,必须基于2个框架进行开发 (1)Map Kit :用于地图展示 (2)Core Location :用于地理定位 3.两个热门专业术语 (1)LBS :Location Based Service(基于定位的服务) (2)SoLoMo :Social Local Mobi

  • IOS CoreLocation实现系统自带定位的方法

    Core Location是iOS SDK中一个提供设备位置的框架.可以使用三种技术来获取位置:GPS.蜂窝或WiFi.在这些技术中,GPS最为精准,如果有GPS硬件,Core Location将优先使用它.如果设备没有GPS硬件(如WiFi iPad)或使用GPS获取当前位置时失败,Core Location将退而求其次,选择使用蜂窝或WiFi. Core Location的大多数功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器来指定位置更新的频率和精度,以及

  • Joomla调用系统自带编辑器的实现方法

    本文实例讲述了Joomla调用系统自带编辑器的方法.分享给大家供大家参考,具体如下: 调用Joomla系统带的编辑器: 路径: /libraries/joomla/html/editor.php 类:JEditor 函数: void display( string $name, string $html, string $width, string $height, int $col, int $row, [boolean $buttons = true], [string $id = null

  • iOS 将系统自带的button改装成上图片下文字的样子

    经常会用到上面是图片,下面是文字的Button.这样的控件可以自定义,但是偶然发现一个直接对系统button进行图片与位置的重新layout实现同样效果的代码,最后使用的按钮是这样的: 代码是通过继承UIButton,然后再重写layoutSubviews方法,对自带的图片和titleLabel进行重新的layout,代码如下: // // ZZZUpDownButton.h // // Copyright © 2016年 George. All rights reserved. // /**

  • IOS提醒用户重新授权打开定位功能

    iOS 8及以上版本最不为人知的一个特点是与应用设置的深层链接,用户可以根据APP的需要授权启用位置.通知.联系人.相机.日历以及健康等设置. 大多数应用程序仅仅是弹出一个包含操作指令的警示窗口,如"进入设置>隐私>位置>OUR_APP".例如,推特的应用程序有一个更为精致和友好的指示对话框,所以我就把它当做一个例子来使用(可惜大多数应用程序都会有一个非常糟糕的版本). 我现在以一个心情沮丧用户的身份写这个帖子,希望更多的iOS开发者能与用户设置建立直接的深层链接,尤

  • iOS中获取系统相册中的图片实例

    本文介绍了iOS中获取系统相册中的图片,在很多应用中都能用到,可以获取单张图片,也可以同时获取多张图片,废话不多说了,看下面吧. 一.获取单张图片 思路: 1.利用UIImagePickerController可以从系统自带的App(照片\相机)中获得图片 2.设置代理,遵守代理协议 注意这个UIImagePickerController类比较特殊,需要遵守两个代理协议 @interface ViewController () <UIImagePickerControllerDelegate,

  • iOS实现获取系统iTunes音乐的方法示例

    播放音乐库中的音乐 音乐是iOS的重要组成播放,无论是iPod.iTouch.iPhone还是iPad都可以在iTunes购买音乐或添加本地音乐到音乐库中同步到你的iOS设备. 本文将给大家详细介绍关于iOS获取系统iTunes音乐的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先来看看效果图 简介 获取类型iTune音乐非常类似于UIKit框架中UIImagePickerController图片选取器的用法,既可以直接使用系统自带的媒体选择器也可以只获取系统的数

  • iOS 10拨打系统电话弹出框延迟出现问题的解决

    前言 最近在开发中遇到了一些问题,发现iOS 10拨打系统电话发现弹出框会延迟2s左右出现,很不爽,研究了一下,发现是openURL在iOS 10及其之后会阻塞主线程 所以,拨打电话前,做个判断,下面话不多说了,来一起看看详细的介绍吧. 示例代码: // 拨打电话 + (void)callPhone:(NSString *)phoneNum { if ([ISNULL(phoneNum) length] == 0) { [SVProgressHUD showErrorWithStatus:@"拨

  • JavaScript获取系统自带的颜色选择器功能(图)

    效果如图所示:下面是获取系统自带的颜色选择器的代码: function $(obj) { return document.getElementById(obj); } function pickColor() { if (!window.isIE) return; var sColor = $('dlgHelper').ChooseColorDlg(); var color = sColor.toString(16); while (color.length [Ctrl+A 全选 注:如需引入外

  • 用系统自带工具在安装系统时给新硬盘分区图解_图解硬盘分区

    用系统自带工具在安装系统时给新硬盘分区图解 screen.width-461) window.open('/upload/20071011135718630.jpg');" src="http://files.jb51.net/upload/20071011135718630.jpg" border=0> screen.width-461) window.open('/upload/20071011135719793.jpg');" src="htt

  • 两种iOS调用系统发短信的方法

    一.程序外调用系统发短信 这个方法其实很简单,直接调用openURL即可: NSURL *url = [NSURL URLWithString:@"sms://15888888888"]; [[UIApplication sharedApplication]openURL:url]; 二.程序内调用系统发短信 这种方法有一个好处就是用户发短信之后还可以回到App. 首先要导入MessageUI.framework,并引入头文件: #import <MessageUI/Messag

随机推荐