简介 在移动互联网时代,移动 app 能解决用户的和诺生活琐事,比如:
周边:找参观,找 KTV、找电影院等等
导航:根据用户设定的起点和中点,进行路线桂法,并指引用户如何到达
在上述应用中,都用到了定位和地图功能,在 iOS 开发中,要想加入这两大功能,必须基于2个框架进行开发
CoreLocation:用于地理定位,地理编码,区域监听等(着重功能实现) MapKit:用于地图展示,例如大头针,路线、覆盖层展示等(着重界面展示)
地理编码:将详细地址转换成经纬度 反地理编码:将经纬度转换成详细地址
2个热门专业术语
LBS:Location Based Service SoLoMo:Social Local Mobile(索罗门)
CoreLocation在 iOS8.0之前的简单实用 使用须知: CoreLocation 框架中所有数据类型的前缀都是 CL CoreLocation 中使用 CLLocationManager 对象来做用户定位
注意: 用户隐私保护,需要提高用户点击允许获取位置信息的成功率。需要在 info.plist 加入字段 Privacy - Location Usage Description 值为:会出现在弹框的中间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 - (CLLocationManager *)locationManager { if (_locationManager == nil ) { _locationManager = [[CLLocationManager alloc] init]; _locationManager.delegate = self ; _locationManager.distanceFilter = 100 ; _locationManager.desiredAccuracy = kCLLocationAccuracyBest; } return _locationManager; }
注意,后台持续运行: (iOS8.0之前的方法)
CLLocationManagerDelegate 更新到位置之后调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 - (void )locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray <CLLocation *> *)locations { NSLog (@"定位到了" ); [manager stopUpdatingLocation]; } - (void )locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus )status { switch (status) { case kCLAuthorizationStatusNotDetermined: { NSLog (@"用户还未决定" ); break ; } case kCLAuthorizationStatusRestricted: { NSLog (@"访问受限" ); break ; } case kCLAuthorizationStatusDenied: { if ([CLLocationManager locationServicesEnabled]) { NSLog (@"定位开启,但被拒" ); }else { NSLog (@"定位关闭,不可用" ); } break ; } case kCLAuthorizationStatusAuthorizedAlways: { NSLog (@"获取前后台定位授权" ); break ; } case kCLAuthorizationStatusAuthorizedWhenInUse: { NSLog (@"获得前台定位授权" ); break ; } default : break ; } } - (void )locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog (@"更新失败" ); }
获取到手机朝向时调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 - (CLLocationManager *)locationManager { if (_locationManager == nil ) { _locationManager = [[CLLocationManager alloc] init]; _locationManager.delegate = self ; _locationManager.headingFilter = 2 ; } return _locationManager; } - (void )locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { NSLog (@"%.2f" , newHeading.magneticHeading); CGFloat angle = newHeading.magneticHeading; CGFloat angleRadian = angle / 180.0 * M_PI; [UIView animateWithDuration:0.25 animations:^{ self .compassImageView.transform = CGAffineTransformMakeRotation (-angleRadian); }]; }
代理方法中有一个 CLHeading
的类,此类用法如下:
1 2 3 4 5 @property (readonly , nonatomic ) CLLocationDirection magneticHeading;@property (readonly , nonatomic ) CLLocationDirection trueHeading;
进入或离开区域时调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (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 { }
iOS8.0之后的适配 从 iOS8.0开始,苹果进一步加强了对用户隐私保护,当 APP 想访问用户的隐私信息时,系统不再自动弹出一个对话框让用户授权
解决方案:
一、调用 iOS8.0的 APL 主动请求用户授权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0 ) { [_locationManager requestAlwaysAuthorization]; [_locationManager requestWhenInUseAuthorization]; } if ([_locationManager respondsToSelector:@selector (requestAlwaysAuthorization)]) { [_locationManager requestAlwaysAuthorization]; [_locationManager requestWhenInUseAuthorization]; }
iOS9.0之后的适配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [self .locationManager requestLocation];
CLLocation 详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @property (readonly , nonatomic ) CLLocationCoordinate2D coordinate;@property (readonly , nonatomic ) CLLocationDistance altitude;@property (readonly , nonatomic ) CLLocationAccuracy horizontalAccuracy;@property (readonly , nonatomic ) CLLocationAccuracy verticalAccuracy;@property (readonly , nonatomic ) CLLocationDirection course;@property (readonly , nonatomic ) CLLocationSpeed speed;@property (readonly , nonatomic , copy ) NSDate *timestamp;@property (readonly , nonatomic , copy , nullable ) CLFloor *floor;@property (nonatomic , readonly , copy ) NSString *description;- (CLLocationDistance )getDistanceFrom:(const CLLocation *)location - (CLLocationDistance )distanceFromLocation:(const CLLocation *)location
示例一 做一个获取一个人的方向、移动多少米、并且打印
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 CLLocation *location = [locations lastObject];NSInteger direction = location.course / 90 ;NSString *directionStr = nil ;switch (direction) { case 0 : directionStr = @"北偏东" ; break ; case 1 : directionStr = @"东偏南" ; break ; case 2 : directionStr = @"南偏西" ; break ; case 3 : directionStr = @"西偏北" ; break ; default : break ; } NSInteger angle = (NSInteger )location.course % 90 ;directionStr = [NSString stringWithFormat:@"%@%zd度" , directionStr, angle]; if (angle == 0 ) { NSRange range = NSMakeRange (0 , 1 ); NSString *subStr = [directionStr substringWithRange:range]; directionStr = [NSString stringWithFormat:@"正%@" , subStr]; } double distance = 0.0 ;if (self .oldLocation != nil ) { distance = [location distanceFromLocation:self .oldLocation]; } self .oldLocation = location;NSString *locationStr = [NSString stringWithFormat:@"%@方向,移动了%.2f米" , directionStr, distance];NSLog (@"%@" , locationStr);
示例二 指南针
1 2 3 4 5 6 7 8 9 10 11 NSLog (@"%.2f" , newHeading.magneticHeading); CGFloat angle = newHeading.magneticHeading; CGFloat angleRadian = angle / 180.0 * M_PI; [UIView animateWithDuration:0.25 animations:^{ self .compassImageView.transform = CGAffineTransformMakeRotation (-angleRadian); }];
区域监听 CLCircularRegion 1 2 3 4 5 6 7 CLLocationCoordinate2D circleCenter = {21.13 , 123.456 };CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:circleCenter radius:1000 identifier:@"lizhao" ];[self .locationManager startMonitoringForRegion:region]; [self .locationManager requestStateForRegion:region];
CLCircularRegion 1 2 3 4 - (instancetype )initWithCenter:(CLLocationCoordinate2D )center radius:(CLLocationDistance )radius identifier:(NSString *)identifier;
地理编码和反地理编码 地理编码需要接触到一个新的类 CLGeocoder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (void )reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler )completionHandler; - (void )geocodeAddressDictionary:(NSDictionary *)addressDictionary completionHandler:(CLGeocodeCompletionHandler )completionHandler; - (void )geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler )completionHandler; - (void )geocodeAddressString:(NSString *)addressString inRegion:(nullable CLRegion *)region completionHandler:(CLGeocodeCompletionHandler )completionHandler; - (void )cancelGeocode;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [self .geoC geocodeAddressString:@"小码哥" completionHandler:^(NSArray <CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { if (error == nil ) { NSLog (@"%@" , placemarks); [placemarks enumerateObjectsUsingBlock:^(CLPlacemark * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog (@"%@" , obj.name); self .addressTV.text = obj.name; self .laTF.text = @(obj.location.coordinate.latitude).stringValue; self .longTF.text = @(obj.location.coordinate.longitude).stringValue; }]; }else { NSLog (@"cuowu--%@" , error.localizedDescription); } }];
第三方框架 INTULocationManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyRoom timeout:10 block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) { if (status == INTULocationStatusSuccess) { NSLog (@"%@" , currentLocation); }else { NSLog (@"定位失败" ); } }]; INTULocationRequestID requestID = [[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyRoom timeout:10 delayUntilAuthorized:YES block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) { if (status == INTULocationStatusSuccess) { NSLog (@"%@" , currentLocation); }else { NSLog (@"定位失败" ); } }]; [[INTULocationManager sharedInstance] cancelLocationRequest:requestID];