前言: 本章会使用OC和Swift分别进行实现,需要了解Swift的小伙伴可以翻一下之前的博文
OC:
// 为了全局只使用一个位置管理者,我们先对CLLocationManager进行懒加载 - (CLLocationManager *)locationM { if (_locationM == nil) { // 创建位置管理者 _locationM = [[CLLocationManager alloc] init]; // 设置代理 _locationM.delegate = self; } return _locationM; } // 在按钮点击事件中开启定位服务 // start:开启服务 stop:关闭服务 // 一旦调用这个方法,就会不断的调用用户信息(因为distanceFilter属性的默认值为-1) // 基本定位(基于Wifi/GPS) [self.locationM startUpdatingLocation]; // 这个方法是用来监听重大位置改变(因为基站与基站之间相距大所以这个方法会通过基站进行定位,前提是有电话模块的支持) // [self.locationM startMonitoringSignificantLocationChanges]; // 先遵守CLLocationManagerDelegate协议,实现下面代理方法 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { NSLog(@"已定位到"); // 定位是非常频繁的,所以获取到用户信息后,最好马上关闭停止定位,以达到省电效果,在适当的时候再重新打开定位 [manager stopUpdatingLocation]; self.locationM = nil; }Swift:
// MARK:- 懒加载 private lazy var locationM : CLLocationManager = { // 创建位置管理者 let locationM = CLLocationManager() // 设置代理 locationM.delegate = self return locationM }() override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { // 使用位置管理者获取用户位置信息 // 根据苹果的习惯,一般方法命中带ing(现在进行时),说明一旦执行这个方法,系统就会不断的调用这个方法 // 默认情况下只会在前台进行定位,如果在后台也想要获取用户的位置,需要开启后台模式 location updates locationM.startUpdatingLocation() } // MARK:- CLLocationManagerDelegate extension ViewController : CLLocationManagerDelegate { // manager : 位置管理者 // locations : 位置数组 func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("已定位到") // 关闭定位 manager.stopUpdatingLocation() }如果想要在后台继续进行定位,需要打开后台的定位模式
拓展:
标准的定位服务(基于GPS/Wifi/基站的定位服务) 程序被完全关闭后就无法再获取位置信息显著位置变化定位服务(基于基站的定位服务,设备必须有电话模块支持) 当APP被完全关闭后,也可以接收到位置通知,并且让APP进入后台处理定位精度相对于标准定位服务较低,耗电量小,更新的频率根据当前位置附近的基站密度决定OC:
- (CLLocationManager *)manager { if (_manager == nil) { _manager = [[CLLocationManager alloc] init]; _manager.delegate = self; // 需要注意的是,必须在info.plist文件中配置’NSLocationWhenInUseUsageDescription‘这个key,否则下面方法无效(官方注释有提到) // 请求前台授权 [_manager requestWhenInUseAuthorization]; // 请求前后台授权 // [_manager requestAlwaysAuthorization]; } return _manager; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.manager startUpdatingLocation]; } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"定位到了"); [self.manager stopUpdatingLocation]; }swift:
class ViewController: UIViewController { lazy var locationMgr : CLLocationManager = { let locationMgr = CLLocationManager() locationMgr.delegate = self // 记得设置相应的授权请求Key // 请求前台定位授权 locationMgr.requestWhenInUseAuthorization() // 请求前后台定位授权 locationMgr.requestAlwaysAuthorization() return locationMgr }() override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { // 开启定位 locationMgr.startUpdatingLocation() } } //MARK: - CLLocationManager代理 extension ViewController : CLLocationManagerDelegate { // 当定位到位置后调用 func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("定位到了") manager.stopUpdatingLocation() } }OC:
if ([_manager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { // 需要注意的是,必须在info.plist文件中配置’NSLocationWhenInUseUsageDescription‘这个key,否则下面方法无效(官方注释有提到) // 请求前台授权 [_manager requestWhenInUseAuthorization]; } if ([_manager respondsToSelector:@selector(requestAlwaysAuthorization)]) { // 请求前后台授权(无论是否开启后台模式都可以获取位置信息,并且不会出现蓝条提示) [_manager requestAlwaysAuthorization]; }swift:
// 记得设置相应的授权请求Key // 根据当前系统版本适配 // 当前版本是8.0及以上 if #available(iOS 8.0, *) { // 请求前台定位授权 locationMgr.requestWhenInUseAuthorization() } if #available(iOS 8.0, *) { // 请求前后台定位授权 locationMgr.requestAlwaysAuthorization() } iOS9定位变化 前台定位于iOS8无变化 后台定位 方法一:在前台定位授权基础上,勾选后台模式location updates之后,需要额外设置属性allowsBackgroundLocationUpdates = YES方法二:直接请求前后台定位授权,设置属性allowsBackgroundLocationUpdates = YES,开启后台模式OC:
// 当授权状态发生改变时调用 - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { switch (status) { case kCLAuthorizationStatusNotDetermined: NSLog(@"用户未选择"); break; // 暂时没用,应该是苹果预留接口 case kCLAuthorizationStatusRestricted: NSLog(@"受限制"); break; // 真正被拒绝、定位服务关闭等影响定位服务的行为都会进入被拒绝状态 case kCLAuthorizationStatusDenied: if ([CLLocationManager locationServicesEnabled]) { // 定位服务开启 NSLog(@"真正被用户拒绝"); // 跳转到设置界面 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { // url地址可以打开 [[UIApplication sharedApplication] openURL:url]; } } else { NSLog(@"服务未开启"); } break; case kCLAuthorizationStatusAuthorizedAlways: NSLog(@"前后台定位授权"); break; case kCLAuthorizationStatusAuthorizedWhenInUse: NSLog(@"前台定位授权"); break; default: break; } }swift:
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { switch status { case .NotDetermined: print("用户未选择") case .Restricted: print("受限制") case.Denied: print("被拒绝") if CLLocationManager .locationServicesEnabled() { // 定位服务开启 print("用户真正拒绝") // 跳转到设置界面 if #available(iOS 8.0, *) { let url = NSURL(string: UIApplicationOpenSettingsURLString) if UIApplication.sharedApplication().canOpenURL(url!) { UIApplication.sharedApplication().openURL(url!) } } } else { print("服务未开启") } case .AuthorizedAlways: print("前后台定位授权") case .AuthorizedWhenInUse: print("前台定位授权") } }结果:维度、经度、海拔(负值表示当前海拔无效)速度(负)航向(从0~359.9) 位置时间
根据获取的位置信息计算用户行走方向,行走距离,偏移角度 coordinate:经纬度信息altitude:海拔horizontalAccuracy:水平方向精度,值为负数时,表示无效verticalAccuracy:判断海拔是否为负数,负数无效course:航向(0~359.9)floor:楼层(使用的楼层需要注册,否则无法使用)distanceFromLocation:计算2点之间的物理直线距离OC:
// 获取当前位置信息 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { /* * coordinate:经纬度信息 * altitude:海拔 * horizontalAccuracy:水平方向精确度,值如果小于0,代表位置数据无效 * verticalAccuracy:判断海拔是否为负数,负数无效 * floor:楼层,使用的楼层需要注册,否则无法使用 * course:航向(0~359.9)(这里的0表示的是正北不是磁北) * distanceFromLocation:计算2各店之间物理直线距离 */ // 获取当前位置信息 CLLocation *locationC = locations.lastObject; // 判断水平数据是否有效 if (locationC.horizontalAccuracy < 0) { // 负数表示无效 return; } // 计算行走方向(北偏东,东偏南,南偏西,西偏北) NSArray *courseAry = @[@"北偏东", @"东偏南", @"南偏西", @"西偏北"]; // 将当前航向值/90度会得到对应的值(0,1,2,3) NSInteger i = locationC.course / 90; // 取出对应航向 NSString *courseStr = courseAry[i]; // 计算偏移角度 NSInteger angle = (int)locationC.course % 90; // 判断是否为正方向 // 对角度取余,为0表示正 if (angle == 0) { // 截取字符串第一个字 courseStr = [courseStr substringToIndex:1]; // 拼接字符串 courseStr = [@"正" stringByAppendingString:courseStr]; } // 计算移动多少米 CGFloat distance = [locationC distanceFromLocation:self.lastLocation]; // 记录上次距离 self.lastLocation = locationC; NSLog(@"向 %@ 方向走了 %lf 米偏移角度 %ld 度", courseStr, distance, angle); }swift:
// 当定位到位置后调用 func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 获取用户当前最新位置 let locationC = locations.last // 判断水平数据是否有效 if locationC?.horizontalAccuracy < 0 { // 负数表示无效 return } // 计算行走方向(北偏东,东偏南,南偏西,西偏北) let courseAry = ["北偏东", "东偏南", "南偏西", "西偏北"] // 将当前航向值/90度会得到相应的值(0,1,2,3) let i = Int((locationC?.course)! / 90) // 取出对应航向 var courseStr = courseAry[i] // 计算偏移角度 let angle = Int((locationC?.course)! % 90) // 判断是否为正方向 // 对角度取余,为0就表示正 if Int(angle) == 0 { // 截取字符串第一个字 courseStr = (courseStr as NSString).substringToIndex(1) } // 确定移动距离 let lastLoc = lastLocation ?? locationC let distance = locationC?.distanceFromLocation(lastLoc!) lastLocation = locationC // 拼接字符串 print("向\(courseStr)方向走了\(distance!)米偏移角度\(angle)") }OC:
- (CLLocationManager *)manager { if (!_manager) { _manager = [[CLLocationManager alloc] init]; _manager.delegate = self; // 请求用户授权区域监听 if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) { [_manager requestAlwaysAuthorization]; } } return _manager; } - (void)viewDidLoad { [super viewDidLoad]; // 判断区域监听是否可用 if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) { return; } // 创建一个区域 // 确定圆心 CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.23, 123.345); // 确定半径 CLLocationDistance distance = 1000.0; // 因为监听区域有最大值,所以要判断下是否超过监听的最大值 if (distance > self.manager.maximumRegionMonitoringDistance) { distance = self.manager.maximumRegionMonitoringDistance; } CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:distance identifier:@"123"]; // 开始监听区域 [self.manager startMonitoringForRegion:region]; } // 进入区域时 - (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { NSLog(@"进入区域%@",region.identifier); } // 离开区域时 - (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { NSLog(@"离开区域%@",region.identifier); } // 但外界调用请求某个指定区域的状态时 - (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region { if (state == CLRegionStateUnknown) { NSLog(@"未识别"); } if (state == CLRegionStateInside) { NSLog(@"在区域内"); } if (state == CLRegionStateOutside) { NSLog(@"在区域外"); } }swift:
lazy var locationMgr : CLLocationManager = { let locationMgr = CLLocationManager() locationMgr.delegate = self // 记得设置相应的授权请求Key // 当前版本是8.0及以上 if #available(iOS 8.0, *) { // 请求前后台定位授权 locationMgr.requestAlwaysAuthorization() } return locationMgr }() override func viewDidLoad() { super.viewDidLoad() // 创建一个区域 // 确定圆心 let center = CLLocationCoordinate2DMake(21.23, 123.345) // 确定半径 var distance : CLLocationDistance = 1000 // 因为监听区域有最大值,索引先判断是否超过了监听区域的最大值 if distance > locationMgr.maximumRegionMonitoringDistance { distance = locationMgr.maximumRegionMonitoringDistance } let region = CLCircularRegion(center: center, radius: distance, identifier: "123") // 判断取余监听是否可用 if CLLocationManager.isMonitoringAvailableForClass(region.classForCoder) { // 开始监听区域 locationMgr.startMonitoringForRegion(region) } } // 进入区域 func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) { print("进入监听区域") } // 离开区域 func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) { print("离开监听区域") } // 区域状态改变 func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) { if state == .Unknown { print("未识别") } if state == .Inside { print("在区域内") } if state == .Outside { print("在区域外") } } 注意: 必须请求用户定位授权使用前先判断区域监听是否可用判断区域半径是否大于最大监听区域,如果大于最大监听区域范围,则无法监听成功OC:
CLGeocoder *geocoder = [[CLGeocoder alloc] init]; // 地理编码 [geocoder geocodeAddressString:@"福建省厦门市" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark *placeM = [placemarks lastObject]; NSLog(@"维度:%@ -- 经度:%@", @(placeM.location.coordinate.latitude).stringValue, @(placeM.location.coordinate.longitude).stringValue); }]; CLGeocoder *geocoder = [[CLGeocoder alloc] init]; // 反地理编码 CLLocationDegrees latitude = 24.490474; CLLocationDegrees longitude = 118.11022; CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { NSLog(@"地址:%@", [placemarks firstObject].name); }];swift:
let geocoder = CLGeocoder() // 地理编码 geocoder.geocodeAddressString("福建省厦门市") { (placemarks, error) in let placeM = placemarks?.last print("维度\(placeM?.location?.coordinate.latitude) -- 经度\(placeM?.location?.coordinate.longitude)") } // 反地理编码 let latitude : CLLocationDegrees = 24.490474 let longitude : CLLocationDegrees = 118.11022 let location = CLLocation(latitude: latitude, longitude: longitude) geocoder.reverseGeocodeLocation(location) { (placemarks, error) in print("地址:\(placemarks?.first?.name)") } }先到这,最近太忙,过两天找个时间根据定位做个小项目再分享出来小项目地址
转载于:https://www.cnblogs.com/miaomiaoshen/p/5536751.html
