第19章 Core Location 和 Map Kit 01 Capabilities 下
CLLocation+Description.h
//
// CLLocation+Description.h
// CLLocationManagerTest
//
// Created by ranzhou on 16/7/8.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import <CoreLocation/CoreLocation.h>
@interface CLLocation (Description)
- (void)descripeCLLocation:(CLLocation*)location;
@end
CLLocation+Description.m
//
// CLLocation+Description.m
// CLLocationManagerTest
//
// Created by ranzhou on 16/7/8.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import "CLLocation+Description.h"
// Core Location
@implementation CLLocation (Description)
- (void)descripeCLLocation:(CLLocation*)location {
// 地理坐标 // coordinate |kəʊˈɔːdɪnət| noun 坐标
CLLocationCoordinate2D coordinate = location.coordinate;
// 纬度 // latitude |ˈlætɪtjuːd| noun 纬度
NSLog(@"%f",coordinate.latitude);
// 经度 // longitude |ˈlɒndʒɪtjuːd| noun 经度
NSLog(@"%f",coordinate.longitude);
/*
水平精度,horizontalAccuracy描述了以coordinate为圆心的圆的半径,horizontalAccuracy越小精度越高。
若horizontalAccuracy为负值,则表示由于某种原因,而导致无法信任coordinate的值。
horizontal |ˌhɒrɪˈzɒntl| adjective 水平的。
accuracy |ˈækjərəsi| noun Uncountable 精确。
*/
NSLog(@"%f",location.horizontalAccuracy);
// 海拔高度 // altitude |ˈæltɪtjuːd, American -tuːd| noun 海拔
NSLog(@"%f",location.altitude);
// 垂直精度,表示海拔高度的精度,如果值为负的,则表示无法获取有效海拔高度。
NSLog(@"%f",location.verticalAccuracy);
// 时间戳,描述的是位置管理器确定位置的时间。
NSLog(@"%@",[location.timestamp dateByAddingTimeInterval:[[NSTimeZone localTimeZone]secondsFromGMT]]);
}
@end
BIDPlace.h
//
// BIDPlace.h
// CLLocationManagerTest
//
// Created by ranzhou on 16/7/7.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
// Map Kit annotation |ˌænəˈteɪʃn| noun 注释
@interface BIDPlace : NSObject<MKAnnotation>
@property (copy,nonatomic) NSString *title;
// subtitle |ˈsʌbtaɪtl| noun 副标题
@property (copy,nonatomic) NSString *subtitle;
@property (nonatomic,assign) CLLocationCoordinate2D coordinate;
@property (nonatomic,strong) CLLocation* location;
@end
BIDPlace.m
//
// BIDPlace.m
// CLLocationManagerTest
//
// Created by ranzhou on 16/7/7.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import "BIDPlace.h"
@implementation BIDPlace
@end
ViewController.m
//
// ViewController.m
// CLLocationManagerTest
//
// Created by ranzhou on 16/7/7.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "BIDPlace.h"
#define TempDescript ([NSString stringWithFormat:@"Class:%@ Cmd:%@ Line:%i",[self class],NSStringFromSelector(_cmd),__LINE__])
@interface ViewController () <CLLocationManagerDelegate,MKMapViewDelegate>
@property (nonatomic,strong) CLLocationManager *locationManager;
@property (nonatomic,strong) MKMapView *mapView;
@property (nonatomic,strong) NSMutableArray<BIDPlace *> *BIDPlaceArry;
@property (nonatomic,assign) double totalDistance;
// -------------------------------------------------------------------
@property (nonatomic,assign) CLLocationCoordinate2D firstUserLocation;
@property (nonatomic,assign) CLLocationCoordinate2D firstLocationResult;
// -------------------------------------------------------------------
@property (nonatomic,strong) NSMutableString *reportStr;
@end
@implementation ViewController
- (void)loadTheMapView
{
self.mapView = [[MKMapView alloc]initWithFrame:self.view.bounds];
// 自动显示用户的位置,Set to YES to add the user location annotation to the map and start updating its location,因为我们获取到的地理位置与真实位置有偏差,所以通过这个方法来取得正确的促初始位置。
self.mapView.showsUserLocation = YES;
self.mapView.delegate = self;
self.mapView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.mapView];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
}
- (void)beginUpdatingLocation
{
if ([CLLocationManager locationServicesEnabled])
{
self.locationManager = [[CLLocationManager alloc] init];
/*
在iOS 8.0下要授权,iOS8以后采用了新的授权方式,需要再Info.plist中注册提示的内容。
*/
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
// NSLocationAlwaysUsageDescription
[self.locationManager requestAlwaysAuthorization];
// NSLocationWhenInUseUsageDescription // authorization |ˌɔːθəraɪˈzeɪʃn| noun 授权、批准
//[self.locationManager requestWhenInUseAuthorization];
}
}
self.locationManager.delegate = self;
// desiredAccuracy是一个double类型,代表m,kCLLocationAccuracyBest表示提供最好的精度。
// desire |dɪˈzaɪə(r)| noun、v 渴望
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// 默认情况下,位置管理器会把检测到的位置更改通知给委托。制定距离筛选器意味着告知位置管理器不要将更改都通知你,仅当位置更改超过特定大小时通知你。设置距离筛选器可以减少应用执行的轮询数量。代表m。
// filter |ˈfɪltə(r)| n 过滤器
self.locationManager.distanceFilter = 20;
// 设置成kCLDistanceFilterNone后将取消前面的distanceFilter相关的设置。恢复到没有筛选器的状态。
// self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.activityType = CLActivityTypeFitness;
self.locationManager.allowsBackgroundLocationUpdates = YES;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
// 启动位置管理器。
[self.locationManager startUpdatingLocation];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.reportStr = [[NSMutableString alloc]init];
[[NSFileManager defaultManager]removeItemAtPath:[self createFileName] error:NULL];
self.totalDistance = 0;
self.BIDPlaceArry = [[NSMutableArray alloc]init];
BIDPlace *bid = [[BIDPlace alloc]init];
bid.title = @"起始位置";
bid.subtitle = @"起始位置";
[self.BIDPlaceArry addObject:bid];
[self loadTheMapView];
[self beginUpdatingLocation];
[self loadButton];
BOOL bb = [CLLocationManager significantLocationChangeMonitoringAvailable];
NSLog(@"%i",bb);
NSLog(@"%i",[UIDevice currentDevice].multitaskingSupported);
}
-(void)loadButton {
UIButton *tempButton = [[UIButton alloc]init];
tempButton.backgroundColor = [[UIColor lightGrayColor]colorWithAlphaComponent:0.5];
[self.view addSubview:tempButton];
[tempButton addTarget:self action:@selector(showReport) forControlEvents:UIControlEventTouchUpInside];
tempButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:tempButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:NULL attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:tempButton attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:NULL attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:40]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:tempButton attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:tempButton attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20]];
}
- (void)showReport
{
//NSString *str = [NSString stringWithContentsOfFile:[self createFileName] encoding:NSUTF8StringEncoding error:NULL];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"日志" message:self.reportStr preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
}
/*
1:对于国内地图而言,使用LocationManager定位所获得经纬度,是有一段较大距离的偏移的,国内地图使用的坐标系统是GCJ-02而ios sdk中所用到的是国际标准的坐标系统WGS-84。因为国内使用的是加密后的坐标系GCJ-02就是网络上叫的火星坐标。
2:locationManager就是因为得到的是火星坐标偏移后的经纬度,所以导致在MapView上有很大的偏差,而在MKMapView上通过定位自己位置所获得的经纬度有是准确,因为apple已经对国内地图做了偏移优化。
3:那么临时的解决方法:想要获得自己准确的经纬度可以直接通过MKMapView中对自身定位来获得:
*/
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
static dispatch_once_t once;
dispatch_once(&once, ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.firstUserLocation = userLocation.coordinate;
// 他告诉地图要显示地图的哪一部分
MKCoordinateRegion coordinateRegion = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 1000, 1000);
[self.mapView setRegion:coordinateRegion animated:YES];
self.BIDPlaceArry.firstObject.coordinate = userLocation.location.coordinate;
[self.mapView addAnnotation:self.BIDPlaceArry.firstObject];
});
});
}
// 无法确定位置时回调的方法。测试发现经常会出现kCLErrorLocationUnknown错误,随后打开原生地图后再打开就没有问题了。然后过一会又会报kCLErrorLocationUnknown错误。关闭屏幕后重新打开,又会恢复。这应该不是一个错误,因该是苹果为了省电自动做的处理。参考:self.locationManager.activityType = CLActivityTypeFitness;
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if (error.code == kCLErrorLocationUnknown)
{
NSLog(@"kCLErrorLocationUnknown");
}
else if(error.code == kCLErrorDenied)
{
// deny |dɪˈnaɪ| v 否认
NSLog(@"kCLErrorDenied");
}
else
{
NSLog(@"Error.code:%li",error.code);
}
}
// overlay |ˌəʊvəˈleɪ|transitive verb 覆盖 |ˈəʊvəleɪ|noun 覆盖物
// polyline ['pɒli:laɪn] n 折线
// render |ˈrendə(r)| v 描述、给某某抹灰
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineRenderer *renderer=[[MKPolylineRenderer alloc]initWithOverlay:overlay];
// stroke |strəʊk|
renderer.strokeColor=[[UIColor redColor]colorWithAlphaComponent:0.5];
renderer.lineWidth=2.0;
return renderer;
}
if([overlay isKindOfClass:[MKCircle class]])
{
MKCircleRenderer *circle = [[MKCircleRenderer alloc]initWithOverlay:overlay];
circle.lineWidth = 2.0;
circle.strokeColor = [UIColor blueColor];
circle.fillColor = [[UIColor blueColor]colorWithAlphaComponent:0.5];
return circle;
}
if([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonRenderer *polygon = [[MKPolygonRenderer alloc]initWithOverlay:overlay];
polygon.lineWidth = 2.0;
polygon.strokeColor = [UIColor yellowColor];
polygon.fillColor = [[UIColor yellowColor]colorWithAlphaComponent:0.5];
return polygon;
}
return nil;
}
/*
manager:第一个参数是该方法的位置管理器。
locations:第二个参数代表的是位置数组,有可能多次位置更新一次性上报,无论何时,数组的最后一项都表示当前位置。
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *lastLocation = locations.lastObject;
// 如果精度太低则放弃次操作。
if (lastLocation.horizontalAccuracy > 66.0 || lastLocation.horizontalAccuracy<0)
{
return;
}
// 在下面的代码中记录第一次获取到的经纬度。
static dispatch_once_t once;
dispatch_once(&once, ^{
self.firstLocationResult = lastLocation.coordinate;
// 记录location是为了计算讲个location之间的距离使用。
self.BIDPlaceArry.firstObject.location = lastLocation;
});
// 如果此时UserLocation还某有更新则return,等待其获取到数据后才继续操作。
if (self.firstUserLocation.longitude==0 || self.firstUserLocation.latitude==0)
{
return;
}
NSMutableArray<CLLocation*> *locationArry = [[NSMutableArray alloc]init];
[locationArry addObject:[self.BIDPlaceArry lastObject].location];
for(int i=0; i<locations.count; i++)
{
CLLocation *location = [locations objectAtIndex:i];
if (location.horizontalAccuracy > 70 || location.horizontalAccuracy<0)
{
continue;
}
// 如果新获取到的地理位置只移动了很小的距离,则不记录。
double newDistance = newDistance = [locationArry.lastObject distanceFromLocation:location];
if (newDistance < 30)
{
continue;
}
[locationArry addObject:location];
}
// 声明一个数组 用来存放画线的点。
CLLocationCoordinate2D coords[locationArry.count];
coords[0] = [self justTheCLLocationCoordinate2D:locationArry.firstObject.coordinate];
for (int i=1; i<locationArry.count; i++)
{
CLLocation *tempLocation = [locationArry objectAtIndex:i];
coords[i] = [self justTheCLLocationCoordinate2D:tempLocation.coordinate];
BIDPlace *newBID = [[BIDPlace alloc]init];
newBID.title = [NSString stringWithFormat:@"%lu",(unsigned long)self.BIDPlaceArry.count];
newBID.coordinate = coords[i];
newBID.location = tempLocation;
double newDistance = [[locationArry objectAtIndex:i-1] distanceFromLocation:tempLocation];
self.totalDistance += newDistance;
newBID.subtitle = [NSString stringWithFormat:@"行程:%f",self.totalDistance];
[self.BIDPlaceArry addObject:newBID];
// 添加新的标记点。
[self.mapView addAnnotation:newBID];
}
// 在不改变缩放级别的情况下,移动到当前位置。
// centerCoordinate allows the coordinate of the region to be changed without changing the zoom level.
[self.mapView setCenterCoordinate:coords[locationArry.count-1] animated:YES];
// 在地图上画线
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:locationArry.count];
[self.mapView addOverlay:polyline level:MKOverlayLevelAboveLabels];
}
- (CLLocationCoordinate2D)justTheCLLocationCoordinate2D:(CLLocationCoordinate2D)coor
{
CLLocationCoordinate2D coord = coor;
coord.longitude = coord.longitude -( self.firstLocationResult.longitude-self.firstUserLocation.longitude);
coord.latitude = coord.latitude - (self.firstLocationResult.latitude - self.firstUserLocation.latitude);
return coord;
}
- (void)reportRecode:(NSString *)str
{
static NSString *path;
if (path==NULL) {
path = [self createFileName];
}
[[NSString stringWithFormat:@"%@\n",str] writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}
- (NSString*)createFileName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.txt",@"调试日志"]];
return filePath;
}
@end