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

来源:互联网 发布:网页游戏挂机软件 编辑:程序博客网 时间:2024/05/01 07:39

首先,对于百度地图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








0 0