您的位置:首页 > 移动开发 > IOS开发

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

2016-07-09 21:58 357 查看
第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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: