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
複制代碼
祝:各位看官身體健康 此緻敬禮!