需求
項目中有這樣的需求,開啟app背景定位和連續定位,将定位的資料傳遞給背景,然後相關主管人員可以檢視公司人員的日常活動軌迹,展示在地圖上
高德地圖的內建和實作
高德地圖的內建和實作:github的連結
效果圖
主要代碼
#Boundle Id
com.star.star
開啟背景定位
//
// AppDelegate.m
// GDMap
//
// Created by zhouyu on 2018/1/22.
// Copyright © 2018年 zhouyu. All rights reserved.
//
#import "AppDelegate.h"
#import "ViewController.h"
#import "ViewController2.h"
#import <MAMapKit/MAMapKit.h>
#import <AMapFoundationKit/AMapFoundationKit.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 高德地圖注冊
[AMapServices sharedServices].apiKey = @"95eea26d4b4e6be311730672da6d49b4";
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController2 alloc] init]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
@end
//
// ViewController.m
// GDMap
//
// Created by zhouyu on 2018/1/22.
// Copyright © 2018年 zhouyu. All rights reserved.
//
#import "ViewController.h"
#import <MAMapKit/MAMapKit.h>
#import "CustomAnnotationView.h"
#import <AMapLocationKit/AMapLocationKit.h>
#define kCalloutViewMargin -8
@interface ViewController ()<MAMapViewDelegate,AMapLocationManagerDelegate>
/*
*
*/
@property (nonatomic, strong) MAMapView *mapView;
@property (nonatomic, strong) NSArray *distanceArray;
@property (nonatomic, assign) double sumDistance;
///全軌迹overlay
@property (nonatomic, strong) MAPolyline *fullTraceLine;
///走過軌迹的overlay
@property (nonatomic, strong) MAPolyline *passedTraceLine;
@property (nonatomic, assign) int passedTraceCoordIndex;
//定位的點經緯度和自定義視圖routeAnno
@property (nonatomic, copy) NSMutableArray *coordsArrM;
@property (nonatomic, copy) NSMutableArray *routeAnnoArrM;
//定位資料管理
@property (nonatomic, strong) AMapLocationManager *locationManager;
/**
* 背景定位是否傳回逆地理資訊,預設NO。
*/
@property (nonatomic, assign) BOOL locatingWithReGeocode;
// 定位
@property (nonatomic, strong) UIButton *locationBtn;
// 使用者自定義大頭針
@property (nonatomic, strong) UIImage *imageLocated;
@property (nonatomic, strong) UIImage *imageNotLocate;
///車頭方向跟随轉動
@property (nonatomic, strong) MAAnimatedAnnotation *car1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"地圖定位";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(cameraDegreeZeroAction)];
///地圖需要v4.5.0及以上版本才必須要打開此選項(v4.5.0以下版本,需要手動配置info.plist)
[AMapServices sharedServices].enableHTTPS = YES;
///把地圖添加至view
[self.view addSubview:self.mapView];
self.mapView.delegate = self;
[self initCoords];
[self initToolBar];
[self initZoom];
[self initLocation];
[self initCurrentPoint];
}
//旋轉成3D立體地圖是複位成3D平面模式
- (void)cameraDegreeZeroAction{
self.mapView.cameraDegree = 0;
}
//彈出層自适應螢幕位置
- (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view{
/* Adjust the map center in order to show the callout view completely. */
if ([view isKindOfClass:[CustomAnnotationView class]]) {
CustomAnnotationView *cusView = (CustomAnnotationView *)view;
CGRect frame = [cusView convertRect:cusView.calloutView.frame toView:self.mapView];
frame = UIEdgeInsetsInsetRect(frame, UIEdgeInsetsMake(kCalloutViewMargin, kCalloutViewMargin, kCalloutViewMargin, kCalloutViewMargin));
if (!CGRectContainsRect(self.mapView.frame, frame)){
/* Calculate the offset to make the callout view show up. */
CGSize offset = [self offsetToContainRect:frame inRect:self.mapView.frame];
CGPoint theCenter = self.mapView.center;
theCenter = CGPointMake(theCenter.x - offset.width, theCenter.y - offset.height);
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:theCenter toCoordinateFromView:self.mapView];
[self.mapView setCenterCoordinate:coordinate animated:YES];
}
}
}
- (CGSize)offsetToContainRect:(CGRect)innerRect inRect:(CGRect)outerRect{
CGFloat nudgeRight = fmaxf(0, CGRectGetMinX(outerRect) - (CGRectGetMinX(innerRect)));
CGFloat nudgeLeft = fminf(0, CGRectGetMaxX(outerRect) - (CGRectGetMaxX(innerRect)));
CGFloat nudgeTop = fmaxf(0, CGRectGetMinY(outerRect) - (CGRectGetMinY(innerRect)));
CGFloat nudgeBottom = fminf(0, CGRectGetMaxY(outerRect) - (CGRectGetMaxY(innerRect)));
return CGSizeMake(nudgeLeft ?: nudgeRight, nudgeTop ?: nudgeBottom);
}
//初始化經緯度資料--需要重背景擷取
- (void)initCoords{
int count = (int)self.coordsArrM.count;
double sum = 0;
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count - 1; ++i) {
NSArray *beginArr = self.coordsArrM[i];
NSArray *endArr = self.coordsArrM[i+1];
CLLocation *begin = [[CLLocation alloc] initWithLatitude:[beginArr[0] doubleValue] longitude:[beginArr[1] doubleValue]];
CLLocation *end = [[CLLocation alloc] initWithLatitude:[endArr[0] doubleValue] longitude:[endArr[1] doubleValue]];
CLLocationDistance distance = [end distanceFromLocation:begin];
[arr addObject:[NSNumber numberWithDouble:distance]];
sum += distance;
}
self.distanceArray = arr;
self.sumDistance = sum;
}
#pragma mark - Map Delegate
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation{
if([annotation isKindOfClass:[MAPointAnnotation class]]) {
NSString *pointReuseIndetifier = @"pointReuseIndetifier3";
CustomAnnotationView *annotationView = (CustomAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndetifier];
if (annotationView == nil) {
annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndetifier];
// 設定為NO,用以調用自定義的calloutView
annotationView.canShowCallout = NO;
// 設定中心點偏移,使得标注底部中間點成為經緯度對應點
// annotationView.centerOffset = CGPointMake(0, -18);
}
//更換展示的圖示
if ([annotation.title isEqualToString:@"route"]) {
annotationView.enabled = YES;
annotationView.image = [UIImage imageNamed:@"trackingPoints"];
} else if ([annotation.title isEqualToString:@"begin"]) {
annotationView.image = [UIImage imageNamed:@"startPoint"];
} else if ([annotation.title isEqualToString:@"end"]) {
annotationView.image = [UIImage imageNamed:@"endPoint"];
} else {
// NSLog(@"%@",annotation);//目前定位點
annotationView.image = [UIImage imageNamed:@"locate"];
}
return annotationView;
}
return nil;
}
//修改軌迹線寬顔色等
- (MAPolylineRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id<MAOverlay>)overlay {
if(overlay == self.fullTraceLine) {
MAPolylineRenderer *polylineView = [[MAPolylineRenderer alloc] initWithPolyline:overlay];
polylineView.lineWidth = 2.f;
polylineView.strokeColor = [UIColor colorWithRed:0 green:0.47 blue:1.0 alpha:0.9];
return polylineView;
}
return nil;
}
//初始化軌迹線路
- (void)mapInitComplete:(MAMapView *)mapView {
int count = (int)self.coordsArrM.count;
CLLocationCoordinate2D *pCoords = malloc(sizeof(CLLocationCoordinate2D) * self.coordsArrM.count);
if(!pCoords) {
return;
}
for(int i = 0; i < self.coordsArrM.count; ++i) {
NSArray *arr = [self.coordsArrM objectAtIndex:i];
CLLocationCoordinate2D *pCur = pCoords + i;
pCur->latitude = [arr[0] doubleValue];
pCur->longitude = [arr[1] doubleValue];
}
self.fullTraceLine = [MAPolyline polylineWithCoordinates:pCoords count:count];
[self.mapView addOverlay:self.fullTraceLine];
NSMutableArray * routeAnno = [NSMutableArray array];
for (int i = 0 ; i < count; i++) {
NSArray *arr = self.coordsArrM[i];
MAPointAnnotation * a = [[MAPointAnnotation alloc] init];
a.coordinate = CLLocationCoordinate2DMake([arr[0] doubleValue], [arr[1] doubleValue]);
if (i == 0) {
a.title = @"begin";
} else if(i == self.coordsArrM.count - 1) {
a.title = @"end";
} else {
a.title = @"route";
}
a.subtitle = [NSString stringWithFormat:@"%d+%@", i, [self getCurrentTimes]];
[routeAnno addObject:a];
}
[self.routeAnnoArrM addObjectsFromArray:routeAnno];
[self.mapView addAnnotations:self.routeAnnoArrM];
[self.mapView showAnnotations:self.routeAnnoArrM animated:YES];
}
//擷取目前的時間
- (NSString*)getCurrentTimes{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
NSDate *datenow = [NSDate date];
NSString *currentTimeString = [formatter stringFromDate:datenow];
return currentTimeString;
}
// MARK: 定位--到目前位置
- (void)actionLocation{
if (self.mapView.userTrackingMode == MAUserTrackingModeFollow){
[self.mapView setUserTrackingMode:MAUserTrackingModeNone animated:YES];
} else {
[self.mapView setCenterCoordinate:self.mapView.userLocation.coordinate animated:YES];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
// 因為下面這句的動畫有bug,是以要延遲0.5s執行,動畫由上一句産生
[self.mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
});
}
}
#pragma mark - 背景定位和持續定位。
- (void)initCurrentPoint{
self.imageLocated = [UIImage imageNamed:@"gpssearchbutton"];
self.imageNotLocate = [UIImage imageNamed:@"gpsnormal"];
self.locationBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, CGRectGetHeight(self.mapView.bounds) - 20, 32, 32)];
self.locationBtn.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
self.locationBtn.backgroundColor = [UIColor whiteColor];
self.locationBtn.layer.cornerRadius = 3;
[self.locationBtn addTarget:self action:@selector(actionLocation) forControlEvents:UIControlEventTouchUpInside];
[self.locationBtn setImage:self.imageNotLocate forState:UIControlStateNormal];
[self.view addSubview:self.locationBtn];
}
#pragma mark - 背景定位和持續定位。
- (void)initLocation{
self.locationManager = [[AMapLocationManager alloc] init];
self.locationManager.delegate = self;//遵守代理,實作協定
//設定定位最小更新距離方法如下,機關米。當兩次定位距離滿足設定的最小更新距離時,SDK會傳回符合要求的定位結果。
self.locationManager.distanceFilter = 0.1;
//開啟持續定位
//iOS 9(不包含iOS 9) 之前設定允許背景定位參數,保持不會被系統挂起
[self.locationManager setPausesLocationUpdatesAutomatically:NO];
//iOS 9(包含iOS 9)之後新特性:将允許出現這種場景,同一app中多個locationmanager:一些隻能在前台定位,另一些可在背景定位,并可随時禁止其背景定位。
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
// 如果需要持續定位傳回逆地理編碼資訊
[self.locationManager setLocatingWithReGeocode:YES];
//開始持續定位
[self.locationManager startUpdatingLocation];
}
#pragma mark - 在回調函數中,擷取定位坐标,進行業務處理--傳遞給背景。
- (void)amapLocationManager:(AMapLocationManager *)manager didUpdateLocation:(CLLocation *)location reGeocode:(AMapLocationReGeocode *)reGeocode{
NSLog(@"location:{緯度lat:%f; 經度lon:%f; accuracy:%f}", location.coordinate.latitude, location.coordinate.longitude, location.horizontalAccuracy);
if (reGeocode){
NSLog(@"reGeocode:%@", reGeocode);
//實時定位,實時繪制定位點連線
// [self.mapView removeAnnotations:self.routeAnnoArrM];
// [self.mapView removeOverlay:self.fullTraceLine];
// [self.coordsArrM addObject:@[@(location.coordinate.latitude),@(location.coordinate.longitude)]];
//
// int count = (int)self.coordsArrM.count;
//
// CLLocationCoordinate2D *pCoords = malloc(sizeof(CLLocationCoordinate2D) * self.coordsArrM.count);
// if(!pCoords) {
// return;
// }
//
// for(int i = 0; i < self.coordsArrM.count; ++i) {
// NSArray *arr = [self.coordsArrM objectAtIndex:i];
// CLLocationCoordinate2D *pCur = pCoords + i;
// pCur->latitude = [arr[0] doubleValue];
// pCur->longitude = [arr[1] doubleValue];
// }
//
// self.fullTraceLine = [MAPolyline polylineWithCoordinates:pCoords count:count];
// [self.mapView addOverlay:self.fullTraceLine];
//
// MAPointAnnotation * a = [[MAPointAnnotation alloc] init];
// a.coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude);
// a.title = @"route";
// a.subtitle = [NSString stringWithFormat:@"+%@", [self getCurrentTimes]];
//
// [self.routeAnnoArrM addObject:a];
// [self.mapView addAnnotations:self.routeAnnoArrM];
// [self.mapView showAnnotations:self.routeAnnoArrM animated:YES];
}
}
//切換地圖顯示級别
- (void)initZoom{
UIView *zoomPannelView = [self makeZoomPannelView];
zoomPannelView.center = CGPointMake(self.view.bounds.size.width - CGRectGetMidX(zoomPannelView.bounds) - 10,
self.view.bounds.size.height - CGRectGetMidY(zoomPannelView.bounds) - 50);
[self.view addSubview:zoomPannelView];
}
- (UIView *)makeZoomPannelView{
UIView *ret = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 53, 98)];
UIButton *incBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 53, 49)];
[incBtn setImage:[UIImage imageNamed:@"increase"] forState:UIControlStateNormal];
[incBtn sizeToFit];
[incBtn addTarget:self action:@selector(zoomPlusAction) forControlEvents:UIControlEventTouchUpInside];
UIButton *decBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 49, 53, 49)];
[decBtn setImage:[UIImage imageNamed:@"decrease"] forState:UIControlStateNormal];
[decBtn sizeToFit];
[decBtn addTarget:self action:@selector(zoomMinusAction) forControlEvents:UIControlEventTouchUpInside];
[ret addSubview:incBtn];
[ret addSubview:decBtn];
return ret;
}
//放大
- (void)zoomPlusAction{
CGFloat oldZoom = self.mapView.zoomLevel;
[self.mapView setZoomLevel:(oldZoom + 1) animated:YES];
}
//縮小
- (void)zoomMinusAction{
CGFloat oldZoom = self.mapView.zoomLevel;
[self.mapView setZoomLevel:(oldZoom - 1) animated:YES];
}
//切換地圖類型
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.navigationController.toolbar.barStyle = UIBarStyleBlack;
self.navigationController.toolbar.translucent = YES;
[self.navigationController setToolbarHidden:NO animated:animated];
}
//切換地圖類型
- (void)mapTypeAction:(UISegmentedControl *)segmentedControl{
self.mapView.mapType = segmentedControl.selectedSegmentIndex;
}
#pragma mark - 切換地圖類型
- (void)initToolBar{
UIBarButtonItem *flexbleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UISegmentedControl *mapTypeSegmentedControl = [[UISegmentedControl alloc] initWithItems: [NSArray arrayWithObjects:@"标準(Standard)",@"衛星(Satellite)",nil]];
mapTypeSegmentedControl.selectedSegmentIndex = self.mapView.mapType;
[mapTypeSegmentedControl addTarget:self action:@selector(mapTypeAction:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *mayTypeItem = [[UIBarButtonItem alloc] initWithCustomView:mapTypeSegmentedControl];
self.toolbarItems = [NSArray arrayWithObjects:flexbleItem, mayTypeItem, flexbleItem, nil];
}
#pragma mark 來加載
- (MAMapView *)mapView{
if (_mapView == nil) {
///初始化地圖
MAMapView *mapView = [[MAMapView alloc] initWithFrame:CGRectMake(0, 44 + [UIApplication sharedApplication].statusBarFrame.size.height, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 44 - [UIApplication sharedApplication].statusBarFrame.size.height)];
_mapView = mapView;
///如果您需要進入地圖就顯示定位小藍點,則需要下面兩行代碼
mapView.showsUserLocation = YES;
mapView.userTrackingMode = MAUserTrackingModeFollow;
mapView.showsCompass= YES; // 設定成NO表示關閉指南針;YES表示顯示指南針
mapView.compassOrigin= CGPointMake(mapView.compassOrigin.x, 10); //設定指南針位置
mapView.showsScale= YES; //設定成NO表示不顯示比例尺;YES表示顯示比例尺
mapView.scaleOrigin= CGPointMake(mapView.scaleOrigin.x - 60, 5); //設定比例尺位置
[mapView setZoomLevel:15.5 animated:YES];
mapView.maxZoomLevel = 19;
//手勢事件
mapView.scrollEnabled = YES;
mapView.zoomEnabled = YES;
mapView.rotateEnabled = YES;
mapView.skyModelEnable = YES;
// 背景持續定位 iOS9以上系統必須配置
// 左側目錄中選中工程名,開啟 TARGETS->Capabilities->Background Modes 在 Background Modes中勾選 Location updates
mapView.allowsBackgroundLocationUpdates = YES;
}
return _mapView;
}
- (NSMutableArray *)routeAnnoArrM{
if (_routeAnnoArrM == nil) {
_routeAnnoArrM = [[NSMutableArray alloc] init];
}
return _routeAnnoArrM;
}
- (NSMutableArray *)coordsArrM{
if (_coordsArrM == nil) {
_coordsArrM = [@[
@[@(39.97617053371078), @(116.3499049793749)],
@[@(39.97619854213431), @( 116.34978804908442)],
@[@(39.97623045687959), @( 116.349674596623)],
@[@(39.976260803938594), @(116.34918981582413)],
@[@(39.97623535890678), @(116.34906721558868)],
@[@(39.976214717128855), @(116.34895185151584)],
@[@(39.976280148755315), @(116.34886935936889)],
@[@(39.97628182112874), @(116.34873954611332)],
@[@(39.97626038855863), @(116.34860763527448)],
@[@(39.97655231226543), @(116.34827643560175)],
@[@(39.976658372925556), @(116.34824186261169)],
@[@(39.9767570732376), @(116.34825080406188)],
@[@(39.976869087779995), @(116.34825631960626)],
@[@(39.97698451764595), @(116.34822111635201)],
@[@(39.977079745909876), @(116.34822901510276)],
@[@(39.97786190186833), @(116.3482045955917)],
@[@(39.977958856930286), @(116.34822159449203)],
@[@(39.97807288885813), @(116.3482256370537)],
@[@(39.978170063673524), @(116.3482098441266)],
@[@(39.978266951404066), @(116.34819564465377)],
@[@(39.978380693859116), @(116.34820541974412)],
@[@(39.97848741209275), @(116.34819672351216)],
@[@(39.978593409607825), @(116.34816588867105)],
@[@(39.97870216883567), @(116.34818489339459)],
@[@(39.979308267469264), @(116.34809495907906)],
@[@(39.97939658036473), @(116.34805113358091)],
@[@(39.979491697188685), @(116.3480310509613)],
@[@(39.979588529006875), @(116.3480082124968)],
@[@(39.979685789111635), @(116.34799530586834)],
@[@(39.979801430587926), @(116.34798818413954)],
@[@(39.97990758587515), @(116.3479996420353)],
@[@(39.980000796262615), @(116.34798697544538)],
@[@(39.980116318796085), @(116.3479912988137)],
@[@(39.98021407403913), @(116.34799204219203)],
@[@(39.980325006125696), @(116.34798535084123)],
@[@(39.98098214824056), @(116.3478962642899)],
@[@(39.98108306010269), @(116.34782449883967)],
@[@(39.98115277119176), @(116.34774758827285)],
@[@(39.98115430642997), @(116.34761476652932)],
@[@(39.98114590845294), @(116.34749135408349)],
@[@(39.98114337322547), @(116.34734772765582)],
@[@(39.98115066909245), @(116.34722082902628)],
@[@(39.98112495260716), @(116.34658043260109)],
@[@(39.9811107163792), @(116.34643721418927)],
@[@(39.981085081075676), @(116.34631638374302)],
@[@(39.981052294975264), @(116.34537348820508)],
@[@(39.980956549928244), @(116.3453513775533)],
] mutableCopy];
}
return _coordsArrM;
}
@end