精通iOS开发--第19章 Core Location 和 Map Kit 01 Capabilities 下 示例程序

来源:互联网 发布:数据错误文件被破坏 编辑:程序博客网 时间:2024/05/07 17:30

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.timestampdateByAddingTimeInterval:[[NSTimeZonelocalTimeZone]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 = [[MKMapViewalloc]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.viewaddSubview:self.mapView];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:self.viewattribute:NSLayoutAttributeLeftrelatedBy:NSLayoutRelationEqualtoItem:self.mapViewattribute:NSLayoutAttributeLeftmultiplier:1.0constant:0.0]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:self.viewattribute:NSLayoutAttributeRightrelatedBy:NSLayoutRelationEqualtoItem:self.mapViewattribute:NSLayoutAttributeRightmultiplier:1.0constant:0.0]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:self.viewattribute:NSLayoutAttributeToprelatedBy:NSLayoutRelationEqualtoItem:self.mapViewattribute:NSLayoutAttributeTopmultiplier:1.0constant:0.0]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:self.viewattribute:NSLayoutAttributeBottomrelatedBy:NSLayoutRelationEqualtoItem:self.mapViewattribute:NSLayoutAttributeBottommultiplier:1.0constant:0.0]];

}


- (void)beginUpdatingLocation

{

    if ([CLLocationManagerlocationServicesEnabled])

    {

        self.locationManager = [[CLLocationManageralloc] init];

        /*

         iOS 8.0下要授权,iOS8以后采用了新的授权方式,需要再Info.plist中注册提示的内容。

         */

        if ([[[UIDevicecurrentDevice] systemVersion]floatValue] >= 8.0)

        {

            // NSLocationAlwaysUsageDescription

            [self.locationManagerrequestAlwaysAuthorization];

            

            // NSLocationWhenInUseUsageDescription // authorization |ˌɔːθəraɪˈzeɪʃn| noun授权、批准

            //[self.locationManager requestWhenInUseAuthorization];

        }

    }

    

    self.locationManager.delegate = self;

    

    // desiredAccuracy是一个double类型,代表mkCLLocationAccuracyBest表示提供最好的精度。

    // desire |dɪˈzaɪə(r)| nounv渴望

    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.locationManagerstartUpdatingLocation];

}


- (void)viewDidLoad

{

    [superviewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    self.reportStr = [[NSMutableStringalloc]init];

    

    [[NSFileManagerdefaultManager]removeItemAtPath:[selfcreateFileName]error:NULL];

    self.totalDistance =0;

    

    self.BIDPlaceArry = [[NSMutableArrayalloc]init];

    BIDPlace *bid = [[BIDPlacealloc]init];

    bid.title =@"起始位置";

    bid.subtitle =@"起始位置";

    [self.BIDPlaceArryaddObject:bid];

    

    

    [selfloadTheMapView];

    [selfbeginUpdatingLocation];

    [selfloadButton];

    

    BOOL bb = [CLLocationManagersignificantLocationChangeMonitoringAvailable];

    NSLog(@"%i",bb);

    

    

    NSLog(@"%i",[UIDevicecurrentDevice].multitaskingSupported);

}


-(void)loadButton {

    UIButton *tempButton = [[UIButtonalloc]init];

    tempButton.backgroundColor = [[UIColorlightGrayColor]colorWithAlphaComponent:0.5];

    [self.viewaddSubview:tempButton];

    [tempButton addTarget:selfaction:@selector(showReport)forControlEvents:UIControlEventTouchUpInside];

    tempButton.translatesAutoresizingMaskIntoConstraints =NO;

    

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:tempButtonattribute:NSLayoutAttributeWidthrelatedBy:NSLayoutRelationEqualtoItem:NULLattribute:NSLayoutAttributeNotAnAttributemultiplier:1.0constant:200]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:tempButtonattribute:NSLayoutAttributeHeightrelatedBy:NSLayoutRelationEqualtoItem:NULLattribute:NSLayoutAttributeNotAnAttributemultiplier:1.0constant:40]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:tempButtonattribute:NSLayoutAttributeCenterXrelatedBy:NSLayoutRelationEqualtoItem:self.viewattribute:NSLayoutAttributeCenterXmultiplier:1.0constant:0]];

    [self.viewaddConstraint:[NSLayoutConstraintconstraintWithItem:self.viewattribute:NSLayoutAttributeBottomrelatedBy:NSLayoutRelationEqualtoItem:tempButton attribute:NSLayoutAttributeBottommultiplier:1.0constant:20]];

}


- (void)showReport

{

    //NSString *str = [NSString stringWithContentsOfFile:[self createFileName] encoding:NSUTF8StringEncoding error:NULL];

    UIAlertController *alertController = [UIAlertControlleralertControllerWithTitle:@"日志"message:self.reportStrpreferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *cancelAction = [UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:nil];

    UIAlertAction *okAction = [UIAlertActionactionWithTitle:@"好的"style:UIAlertActionStyleDefaulthandler:nil];

    [alertController addAction:cancelAction];

    [alertController addAction:okAction];

    [selfpresentViewController:alertControlleranimated:YEScompletion:nil];

}


/*

 1:对于国内地图而言,使用LocationManager定位所获得经纬度,是有一段较大距离的偏移的,国内地图使用的坐标系统是GCJ-02ios sdk中所用到的是国际标准的坐标系统WGS-84。因为国内使用的是加密后的坐标系GCJ-02就是网络上叫的火星坐标。

 2locationManager就是因为得到的是火星坐标偏移后的经纬度,所以导致在MapView上有很大的偏差,而在MKMapView上通过定位自己位置所获得的经纬度有是准确,因为apple已经对国内地图做了偏移优化。

 3:那么临时的解决方法:想要获得自己准确的经纬度可以直接通过MKMapView中对自身定位来获得:

 */

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation

{

    staticdispatch_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.mapViewsetRegion:coordinateRegionanimated:YES];

            


            self.BIDPlaceArry.firstObject.coordinate = userLocation.location.coordinate;

            [self.mapViewaddAnnotation: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 ([overlayisKindOfClass:[MKPolylineclass]])

    {

        MKPolylineRenderer *renderer=[[MKPolylineRendereralloc]initWithOverlay:overlay];

        // stroke |strəʊk|

        renderer.strokeColor=[[UIColorredColor]colorWithAlphaComponent:0.5];

        renderer.lineWidth=2.0;

        return renderer;

    }

    if([overlayisKindOfClass:[MKCircleclass]])

    {

        MKCircleRenderer *circle = [[MKCircleRendereralloc]initWithOverlay:overlay];

        circle.lineWidth =2.0;

        circle.strokeColor = [UIColorblueColor];

        circle.fillColor = [[UIColorblueColor]colorWithAlphaComponent:0.5];

        return circle;

    }

    if([overlayisKindOfClass:[MKPolygonclass]])

    {

        MKPolygonRenderer *polygon = [[MKPolygonRendereralloc]initWithOverlay:overlay];

        polygon.lineWidth =2.0;

        polygon.strokeColor = [UIColoryellowColor];

        polygon.fillColor = [[UIColoryellowColor]colorWithAlphaComponent:0.5];

        return polygon;

    }

    returnnil;

}


/*

 manager:第一个参数是该方法的位置管理器。

 locations:第二个参数代表的是位置数组,有可能多次位置更新一次性上报,无论何时,数组的最后一项都表示当前位置。

 */

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {


    CLLocation *lastLocation = locations.lastObject;

    

    // 如果精度太低则放弃次操作。

    if (lastLocation.horizontalAccuracy >66.0 || lastLocation.horizontalAccuracy<0)

    {

        return;

    }

    // 在下面的代码中记录第一次获取到的经纬度。

    staticdispatch_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 = [[NSMutableArrayalloc]init];

    [locationArry addObject:[self.BIDPlaceArrylastObject].location];

    for(int i=0; i<locations.count; i++)

    {

        CLLocation *location = [locationsobjectAtIndex:i];

        if (location.horizontalAccuracy >70 || location.horizontalAccuracy<0)

        {

            continue;

        }

        

        // 如果新获取到的地理位置只移动了很小的距离,则不记录。

        double newDistance = newDistance = [locationArry.lastObjectdistanceFromLocation:location];

        if (newDistance <30)

        {

            continue;

        }

        

        [locationArry addObject:location];

    }

    

    // 声明一个数组 用来存放画线的点。

    CLLocationCoordinate2D coords[locationArry.count];

    coords[0] = [selfjustTheCLLocationCoordinate2D:locationArry.firstObject.coordinate];

    for (int i=1; i<locationArry.count; i++)

    {

        CLLocation *tempLocation = [locationArryobjectAtIndex:i];

        coords[i] = [selfjustTheCLLocationCoordinate2D:tempLocation.coordinate];

        


        BIDPlace *newBID = [[BIDPlacealloc]init];

        newBID.title = [NSStringstringWithFormat:@"%lu",(unsignedlong)self.BIDPlaceArry.count];

        newBID.coordinate = coords[i];

        newBID.location = tempLocation;

        double newDistance = [[locationArryobjectAtIndex:i-1]distanceFromLocation:tempLocation];

        self.totalDistance += newDistance;

        newBID.subtitle = [NSStringstringWithFormat:@"行程:%f",self.totalDistance];

        [self.BIDPlaceArryaddObject:newBID];

        

        // 添加新的标记点。

        [self.mapViewaddAnnotation:newBID];

    }

    

    // 在不改变缩放级别的情况下,移动到当前位置。

    // centerCoordinate allows the coordinate of the region to be changed without changing the zoom level.

    [self.mapViewsetCenterCoordinate:coords[locationArry.count-1]animated:YES];

    

    // 在地图上画线

    MKPolyline *polyline = [MKPolylinepolylineWithCoordinates:coordscount:locationArry.count];

    [self.mapViewaddOverlay: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 = [pathsobjectAtIndex:0];

    NSString *filePath = [documentsDirectorystringByAppendingPathComponent:[NSStringstringWithFormat:@"%@.txt",@"调试日志"]];

    return filePath;

}


@end

0 0