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

[置顶] iOS百度地图SDK之实时绘制轨迹(后台仍执行)

2015-08-14 11:15 567 查看

首先,对于百度地图SDK的配置和环境搭建就不做说明,需要的人可以博客中另一篇文章看 《iOS百度地图SDK基本使用》 ,本文的重点在于实现实时绘制轨迹的功能,并且对细节进行处理和优化



1、在AppDelegate.m文件中

#import "AppDelegate.h"
// BMapKit.h代表导入了所有的头文件
#import <BaiduMapAPI/BMapKit.h>
@interface AppDelegate ()<BMKGeneralDelegate>
{
BMKMapManager *mapManager;
}
@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 要使用地图,首先需要初始化和启动BMKMapManager
mapManager = [[BMKMapManager alloc]init];
// 填入验证码, 如需要使用网络和授权验证服务,则需要设置代理信息
BOOL ret = [mapManager start:@"在这里填入的是密钥中的安全码" generalDelegate:nil];
if (!ret) {
NSLog(@"地图管理器初始化失败");
}
else{
NSLog(@"初始化成功");
}
return YES;
}


2、在ViewController.m文件中
#import "ViewController.h"
#import <BaiduMapAPI/BMapKit.h>

#define normalLocationTag 1
#define compassLocationTag 2

@interface ViewController () <BMKLocationServiceDelegate , BMKMapViewDelegate>
{
BMKMapView *mapView;
BMKLocationService *locationService;
// 用于手动验证
CLLocationManager *locationManager;
// 用于记录经过的点
NSMutableArray *locationPoint;
// 在地图上绘制的折线
BMKPolyline *routeLine;
// 中间变量->location类型(地理位置)
CLLocation *currentLocation;
}
@end


在viewDidLoad中将两个初始化体现出来
- (void)viewDidLoad {
[super viewDidLoad];

// 1、所有的初始化操作
[self initObjects];

// 2、所有的地图初始操作
[self operationForMap];
}


初始化操作的封装后的方法
- (void)initObjects
{
// 初始化mapView
mapView = [[BMKMapView alloc]initWithFrame:CGRectMake(0, 120, self.view.bounds.size.width, self.view.bounds.size.height)];
// 初始化locationService
locationService = [[BMKLocationService alloc]init];
}


对地图的各个参数进行操作封装后的方法
- (void)operationForMap
{
// 设置过滤距离,更新的最小间隔距离
[BMKLocationService setLocationDistanceFilter:6.0f];

// 设置定位精度模式
/* tips:
以前使用了kCLLocationAccuracyBest--> 表示在使用电池的最高精度
现在使用了kCLLocationAccuracyBestForNavigation--> 表示在外接电源的时候的最高精度(有些在AppStore上架的APP由于过度依赖于高精度的定位,所以采用了这种耗电的定位设置)
*/
[BMKLocationService setLocationDesiredAccuracy:kCLLocationAccuracyBestForNavigation];

// 设置地图类型
mapView.mapType = BMKMapTypeSatellite;

// 设置是否需要热力图显示
[mapView setBaiduHeatMapEnabled:NO];

// 设置是否允许旋转地图
mapView.rotateEnabled = YES;<span style="white-space:pre">												</span>
// .....对于地图还有很多设置,这里不一一列举,需要的话可以在文档中查到<span style="white-space:pre">								</span>
[self.view addSubview:mapView];
}

- (void)operationForLocation:(BMKUserLocation *)userLocation
{
// 1、检查移动的距离,移除不合理的点
if (locationPoint.count > 0) {
CLLocationDistance distance = [userLocation.location distanceFromLocation:currentLocation];
if (distance < 5)
return;
}

// 2、初始化坐标点数组
if (nil == locationPoint) {
locationPoint = [[NSMutableArray alloc] init];
}

// 3、将合理的点添加到数组
[locationPoint addObject:userLocation.location];

// 4、作为前一个坐标位置辅助操作
currentLocation = userLocation.location;

// 5、开始画线
[self configureRoutes];

// 6、实时更新用户位子
[mapView updateLocationData:userLocation];
}


#pragma mark - 点击两个button触发的事件
- (IBAction)startLocation:(UIButton *)button
{
// 由于IOS8中定位的授权机制改变 需要进行手动授权(导致程序无法进行定位的主要原因)
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8) {
locationManager = [[CLLocationManager alloc] init];
[locationManager requestAlwaysAuthorization];
[locationManager requestWhenInUseAuthorization];
}
// 开启用户定位
[locationService startUserLocationService];

// 开始先关闭地位图层(也就是定位的小圆点)-->用户体验问题
mapView.showsUserLocation = NO;

// 根据所点击的button来开启不同的定位模式
if (button.tag == normalLocationTag) {
mapView.userTrackingMode = BMKUserTrackingModeFollow;
}else if(button.tag == compassLocationTag) {
mapView.userTrackingMode = BMKUserTrackingModeFollowWithHeading;
}

mapView.showsUserLocation = YES;

// 1、通过比例调试地图的显示
#if 1
[mapView setZoomEnabled:YES];
mapView.zoomLevel = 19;// 级别是 3-19
#endif

#if 0
// 2、通过范围调试地图的显示
BMKCoordinateRegion adjustRegion = [mapView regionThatFits:BMKCoordinateRegionMake(locationService.userLocation.location.coordinate, BMKCoordinateSpanMake(0.03f,0.03f))];
[mapView setRegion:adjustRegion animated:YES];
#endif
}


#pragma mark - 视图的出现和消失(在其中设置代理和取消代理,优化内存管理)
- (void)viewWillAppear:(BOOL)animated
{
[mapView viewWillAppear];
mapView.delegate = self;
locationService.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated
{
[mapView viewWillDisappear];
// 一般情况下都需要在这里关闭掉代理,但是由于本程序需要在后台继续绘制轨迹,因为对应的代理方法是绘制轨迹,所以继续设置代理
mapView.delegate = self;
locationService.delegate = self;
}


#pragma mark - mapView的协议
- (BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id<BMKOverlay>)overlay
{
if ([overlay isKindOfClass:[BMKPolyline class]]) {
BMKPolylineView *polylineView = [[BMKPolylineView alloc]initWithPolyline:overlay];
// 设置划出的轨迹的基本属性-->也是使得定位看起来更加准确的主要原因
polylineView.strokeColor = [[UIColor blueColor]colorWithAlphaComponent:0.5];
polylineView.fillColor = [[UIColor blueColor]colorWithAlphaComponent:0.8];
polylineView.lineWidth = 6.0;
return polylineView;
}
return nil;
}

#pragma mark - 绘制轨迹
-(void)configureRoutes
{
// 1、分配内存空间给存储经过点的数组
BMKMapPoint* pointArray = (BMKMapPoint *)malloc(sizeof(CLLocationCoordinate2D) * locationPoint.count);

// 2、创建坐标点并添加到数组中
for(int idx = 0; idx < locationPoint.count; idx++)
{
CLLocation *location = [locationPoint objectAtIndex:idx];
CLLocationDegrees latitude  = location.coordinate.latitude;
CLLocationDegrees longitude = location.coordinate.longitude;
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
BMKMapPoint point = BMKMapPointForCoordinate(coordinate);
pointArray[idx] = point;
}
// 3、防止重复绘制
if (routeLine) {
//在地图上移除已有的坐标点
[mapView removeOverlay:routeLine];
}

// 4、画线
routeLine = [BMKPolyline polylineWithPoints:pointArray count:locationPoint.count];

// 5、将折线(覆盖)添加到地图
if (nil != routeLine) {
[mapView addOverlay:routeLine];
}

// 6、清楚分配的内存
free(pointArray);
}

#pragma mark - 更新用户位置时所调用的三种方法
// 更新位置
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
// 说明:由于开启了“无限后台”的外挂模式(^-^)所以可以直接写操作代码,然后系统默认在任何情况执行,但是为了已读,规划代码如下
// 1、活跃状态
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
[self operationForLocation:userLocation];
}else if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
// 2、后台模式
{
[self operationForLocation:userLocation];
}
// 3、不活跃模式
else if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
{
[self operationForLocation:userLocation];
}
}

// 更新方向
- (void)didUpdateUserHeading:(BMKUserLocation *)userLocation
{
[mapView updateLocationData:userLocation];
}

// 定位失败了会调用
- (void)didFailToLocateUserWithError:(NSError *)error
{
NSLog(@"did failed locate,error is %@",[error localizedDescription]);
}


到这里,代码就结束了,在viewWillDisappear中,依然没有关掉代理,是想让程序在锁屏后,或者是点击home键返回到桌面后仍然执行轨迹的绘制,但是仅仅设置代理是不够的,在没有进行配置的情况下,iOS系统最多可以让一个APP在有5秒钟的时间,用来进行保存和清理资源等任务。如果应用调用了UIApplication的beginBackgroundTaskWithExpirationHandler的方法,可以让APP在后台有10分组的运行时间,用来清理本地的缓存或者是进行数据的处理。超过了时间,APP便会被强制挂起

但是,就拿本例中所涉及的实时绘制轨迹来说,比如要开发一款跑步时绘制自己轨迹的应用,总不能跑步的时候都要将手机屏幕开着,并且还不能切换到其他地方,这样无疑增加了大量的电量消耗,所以,苹果也是为以下的几种提供了所谓的“无限后台”的外挂模式

1、Audio(音频播放)
2、Location/GPS(定位的实时更新)
3、Newsstand(杂志下载)
4、VoIP(网络电话)

所以,对于本例来说,苹果是允许启动外挂模式的,下面说明应该怎么配置这种模式

在info.plist文件中添加这样一项,Required background modes,然后这一项默认是数组类型,需要再在这项中添加一个item,默认生成了一项key为item0,接着将这个item0的value变为App registers for location updates
下面给出图例示范:



但是有人会想,那这样的话每个程序都可以开外挂了咯?话虽如此,但是在AppStore审核的时候,如果在plist文件中配置了这样的属性,那么AppStore会检测应用程序是否属于这种类型,如果不是的话,那么这样的APP就会被拒绝。

PS:demo的地址为  http://download.csdn.net/detail/nineteen_/9106725
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: