0.起步 專案版本有內建地圖的開發需求,因此做了一波技術預研。 0.1 MapKit MapKit是蘋果的內建地圖框架,目前在國內使用的是高德地圖提供的服務,所以即便是內建地圖,也能提供較為詳細的地圖資訊。 匯入:
#import <MapKit/MapKit.h>
複製程式碼
0.2 CoreLocation.framework CoreLocation是蘋果提供的導航+定位服務框架,我們在後續開發中需要依仗他來進行地圖導航定位開發。 匯入:
#import <CoreLocation/CoreLocation.h>
複製程式碼
1.內建地圖開發
1.1 MapView 為了實現上圖中的地圖頁面,我們需要通過MapKit提供的MapView來匯入地圖。 在Capabilities中開啟Maps的許可權
在StoryBoard中拖入MapView
為MapView新增代理,並且指定第一次啟動時候載入的地圖方位,例如經緯度為(24.489224794270353f,118.18014079685172f)(6號樓的經緯度)
self.mapView.delegate = self;
self.mapView.mapType = MKMapTypeMutedStandard;
self.mapView.showsUserLocation = YES;
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//CLLocationCoordinate2DMake:引數: 維度、經度、南北方寬度(km)、東西方寬度(km)
double lat = 24.489224794270353f;
double lon = 118.18014079685172f;
[self.mapView setRegion:MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(lat , lon), 300, 194)
animated:YES];
複製程式碼
以上程式碼中,我們看到self.mapView.showsUserLocation = YES;這一步看字面意思是要在地圖上顯示使用者的地理位置。 但在實際的場景中,MapKit本身不提供導航、定位功能,僅提供地圖資訊。所以在此我們需要再引入CoreLocation來提供使用者的位置資訊。
1.2 定位服務 CLLocationManager能夠為我們提供導航定位所需的一些使用者許可權支援,在開啟服務之前,我們需要跟使用者獲取相關的系統許可權。
if (nil == _locationManager)
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0){
[_locationManager requestWhenInUseAuthorization];
}
if(![CLLocationManager locationServicesEnabled]){
NSLog(@"請開啟定位:設定 > 隱私 > 位置 > 定位服務");
}
// 持續使用定位服務
if([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization]; // 永久授權
[_locationManager requestWhenInUseAuthorization]; //使用中授權
}
// 方位服務
if ([CLLocationManager headingAvailable])
{
_locationManager.headingFilter = 5;
[_locationManager startUpdatingHeading];
}
[_locationManager startUpdatingLocation];
複製程式碼
在info.plist中我們需要新增:
Privacy - Location When In Use Usage Description
複製程式碼
當我們呼叫上部分程式碼後之後,我們便能在地圖上看到我們的定位了。 如果一眼看不到,記得拖一拖地圖,並且確定Wifi沒連線代理VPN。(我曾在洛杉磯看到我的位置)
1.3導航服務 關於導航,我們可以提供的便是我們當前MapKit中的線路繪製,或者呼叫系統的地圖服務app,或者呼叫百度地圖、高德地圖這些三方應用。 為了偷懶,我僅僅介紹MapKit繪製地圖和呼叫系統地圖App。 系統內建地圖導航App:
- (void)navByVender {
CLLocation *begin = [[CLLocation alloc] initWithLatitude:[[NSNumber numberWithFloat:self.myPlace.latitude] floatValue]
longitude:[[NSNumber numberWithFloat:self.myPlace.longitude] floatValue]];
[self.geocoder reverseGeocodeLocation:begin completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
__block CLPlacemark * beginPlace = [placemarks firstObject];
CLLocation *end = [[CLLocation alloc] initWithLatitude:[[NSNumber numberWithFloat:self.finishPlace.latitude] floatValue]
longitude:[[NSNumber numberWithFloat:self.finishPlace.longitude] floatValue]];
[self.geocoder reverseGeocodeLocation:end completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if(error) {
NSLog(@"Error Info %@",error.userInfo);
} else {
CLPlacemark * endPlace = [placemarks firstObject];
MKMapItem * beginItem = [[MKMapItem alloc] initWithPlacemark:beginPlace];
MKMapItem * endItem = [[MKMapItem alloc] initWithPlacemark:endPlace];
NSString * directionsMode;
switch (self.navType) {
case 0:
directionsMode = MKLaunchOptionsDirectionsModeWalking;
break;
case 1:
directionsMode = MKLaunchOptionsDirectionsModeDriving;
break;
case 2:
directionsMode = MKLaunchOptionsDirectionsModeTransit;
break;
default:
directionsMode = MKLaunchOptionsDirectionsModeWalking;
break;
}
NSDictionary *launchDic = @{
//範圍
MKLaunchOptionsMapSpanKey : @(50000),
// 設定導航模式引數
MKLaunchOptionsDirectionsModeKey : directionsMode,
// 設定地圖型別
MKLaunchOptionsMapTypeKey : @(MKMapTypeStandard),
// 設定是否顯示交通
MKLaunchOptionsShowsTrafficKey : @(YES),
};
[MKMapItem openMapsWithItems:@[beginItem, endItem] launchOptions:launchDic];
}
}];
}];
}
複製程式碼
導航發起之前,我們需要準備好兩個座標,以上程式碼中,我把使用者自身的地址作為Begin地點,把地圖正中央作為目的地的座標進行導航。(反正你傳兩個座標就對了)
//地理編碼方法
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
// 反地理編碼方法
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
複製程式碼
地理編碼:根據給定的地名,獲得具體的位置資訊(比如經緯度、地址的全稱等) 反地理編碼:根據給定的經緯度,獲得具體的位置資訊 我們需要reverseGeocodeLocation來做地圖的反地理編碼操作,這樣我們傳入的地理座標才被識別為地理位置資訊。
[MKMapItem openMapsWithItems:@[beginItem, endItem] launchOptions:launchDic];
複製程式碼
這一處程式碼,變回喚起系統內建的地圖導航功能。
內建MapKit可繪製的導航方案:
MKPlacemark *fromPlacemark = [[MKPlacemark alloc] initWithCoordinate:self.myPlace addressDictionary:nil];
MKPlacemark *toPlacemark = [[MKPlacemark alloc] initWithCoordinate:self.finishPlace addressDictionary:nil];
MKMapItem *fromItem = [[MKMapItem alloc] initWithPlacemark:fromPlacemark];
MKMapItem *toItem = [[MKMapItem alloc] initWithPlacemark:toPlacemark];
- (void)findDirectionsFrom:(MKMapItem *)from to:(MKMapItem *)to{
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
request.source = from;
request.destination = to;
request.transportType = MKDirectionsTransportTypeWalking;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
//ios7獲取繪製路線的路徑方法
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(@"Error info:%@", error.userInfo[@"NSLocalizedFailureReason"]);
}
else {
for (MKRoute *route in response.routes) {
// MKRoute *route = response.routes[0];
for(id<MKOverlay> overLay in self.mapView.overlays) {
[self.mapView removeOverlay:overLay];
}
[self.mapView addOverlay:route.polyline level:0];
double lat = self.mapView.region.center.latitude;
double lon = self.mapView.region.center.longitude;
double latDelta = self.mapView.region.span.latitudeDelta * 100000;
double lonDelta = self.mapView.region.span.longitudeDelta * 100000;
if(_firstStarNav) {
_firstStarNav = NO;
[self.mapView setRegion:MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(lat , lon), 200, 126)
animated:YES];
}
}
}
}];
}
複製程式碼
在以上方法後,我們可以在以下一個代理中獲得一套地圖路線,我們可以通過以下方式,將繪製到地圖上的線路定製化。
- (MKOverlayRenderer*)mapView:(MKMapView*)mapView rendererForOverlay:(id)overlay {
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.lineWidth = 5;
renderer.strokeColor = HEX_RGBA(0xf26f5f, 1);
return renderer;
}
複製程式碼
這裡補充一下,在地圖上顯示的各種線段繪製之類的呃,都是要在overlay層進行表示的。 我們可控制的線段的寬度、顏色、延續的拐角光滑度、線頭是否圓角。
1.4 地圖中的元素定製 在地圖中我們可以對一些UI方案進行定製。
我們能夠進行完全定製的,是在地圖上的Pin圖釘。
我們可以在一下方法中,對Annotation進行修改。(這個方法堪比 table的那個cellForRow)
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
複製程式碼
在圖釘上方彈出的蘋果稱之為CalloutAccessoryView,這裡我們可以修改的便是左右部分的View,此處可以新增Button或者UIImageView。 我們在這個方法中,還會獲取到使用者個人定位服務下自己的圖釘資訊。此處也可以定製。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView* aView;
if ([annotation isKindOfClass:[MKUserLocation class]]) {
self.myPlace = annotation.coordinate;
return nil;
} else if([annotation isKindOfClass:[MyPinAnnotation class]]) {
aView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"MyPinAnnotation"];
aView.canShowCallout = YES;
aView.image = [UIImage imageNamed:@"pin"];
aView.frame = CGRectMake(0, 0, 50, 50);
UIImageView *myCustomImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon"]];
myCustomImage.frame = CGRectMake(0, 0, 50, 50);
aView.leftCalloutAccessoryView = myCustomImage;
MapMarkBtn *rightButton = [[MapMarkBtn alloc] initWithFrame:CGRectMake(0, 0, 80, 50)];
rightButton.coordinate = annotation.coordinate;
rightButton.backgroundColor = [UIColor grayColor];
[rightButton setTitle:@"到這裡去" forState:UIControlStateNormal];
[rightButton addTarget:self action:@selector(gotoPlace:) forControlEvents:UIControlEventTouchUpInside];
aView.rightCalloutAccessoryView = rightButton;
}
else {
aView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"MKPointAnnotation"];
aView.canShowCallout = YES;
aView.image = [UIImage imageNamed:@"pin"];
aView.frame = CGRectMake(0, 0, 50, 50);
}
return aView;
}
複製程式碼
多說無益,附上地圖功能的Demo地址:
https://github.com/filelife/FLMapKit.git
複製程式碼
祝:各位看官身體健康 此致敬禮!