代码下载地址:这里写链接内容
github地址:这里写链接内容
一.MKMapView基本使用
MapKit 地图开发需要依赖位置信息,所以需要允许访问用户位置信息,具体设置见前面博客。
MKMapView的其他设置如下:
//设置用户的跟踪模式-即用户的位置(默认不跟踪)
[self.mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading];
//设置地图的类型
[self.mapView setMapType:MKMapTypeStandard];
//如果定位成功
if ([FixLocation shareFixLoction].placemark) {
//设置地图的中心点
[self.mapView setCenterCoordinate:[FixLocation shareFixLoction].coordinate];
}
/**
* 下面两个方法需要一个位置,可以在定位成功之后再设置
//设置地图的中心点
[self.mapView setCenterCoordinate:currentLocation.coordinate animated:YES];
//设置地图的显示范围
[self.mapView setRegion:MKCoordinateRegionMake(currentLocation.coordinate, MKCoordinateSpanMake(0.1, 0.1)) animated:YES];
*/
二添加标注(大头针)
1.使用 -(void)addAnnotation:(id )annotation 方法,添加标注。标注对象必须实现协议
@protocol MKAnnotation <NSObject>
//地理坐标
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@optional
//描述的标题
@property (nonatomic, readonly, copy, nullable) NSString *title;
//描述的副标题
@property (nonatomic, readonly, copy, nullable) NSString *subtitle;
//设置地理坐标的方法
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate NS_AVAILABLE(10_9, 4_0);
@end
2.在MapView的代理方法- (MKAnnotationView )mapView:(MKMapView )mapView viewForAnnotation:(id)annotation中我们需要返回标注的视图MKAnnotationView对象或者其子类MKPinAnnotationView(可以做从天而降的动画)对象。而其具体实现类似于UITableViewCell,利用重用机制。
static NSString *identifier = @"MKAnnotationViewID";
//从缓存池中取出可以循环利用的大头针view
MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!view) {
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
//能否显示大头针描述
view.canShowCallout = YES;
//设置大头针描述的偏移量
[view setCalloutOffset:CGPointMake(-8, -8)];
//设置大头针描述的左右控件
[view setLeftCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]];
[view setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeContactAdd]];
//设置从天而降的动画
view.animatesDrop = YES;
}
view.annotation = annotation;
return view;
三.自定义标注(大头针)的描述
实现方案是通过自定义标注来替代标注的描述:即在需要显示标注的描述时,再向此位置添加一个标注来偷梁换柱。在MapView的代理方法- (void)mapView:(MKMapView )mapView didSelectAnnotationView:(MKAnnotationView )view中来操作:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
//如果是标注视图被选中,那么添加描述类型的标注
if ([view isKindOfClass:[MainAnnotationView class]]) {
MainAnnotation *mainAnnotation = (MainAnnotation *)view.annotation;
[self addDescribeAnnotation:mainAnnotation];
}
//如果是描述类型的标注被点击,那么不做任何处理
else if ([view isKindOfClass:[DescribeAnnotationView class]])
{
return;
}
//如果点击的是其他类型的标注,那么移除所有描述类型的标注
else
{
[self removeAllDescribeAnnotationView];
return;
}
}
/**
* 添加一个描述类型的大头针
*
* @param annotation MainAnnotation类型的大头针模型
*/
- (void)addDescribeAnnotation:(MainAnnotation *)annotation
{
//先移除所有的DescribeAnnotationView,模拟系统的一次只能显示一个标注的描述
[self removeAllDescribeAnnotationView];
//添加描述类型的标注DescribeAnnotation
DescribeAnnotation *describeAnnotation = [DescribeAnnotation describeAnnotationWithDic:annotation.infoDic];
describeAnnotation.coordinate = annotation.coordinate;
[self.mapView addAnnotation:describeAnnotation];
}
/**
* 移除所有DescribeAnnotationView
*/
- (void)removeAllDescribeAnnotationView
{
for (id annotation in self.mapView.annotations) {
if ([annotation isKindOfClass:[DescribeAnnotation class]]) {
[self.mapView removeAnnotation:annotation];
}
}
}
四.自定义标注(大头针)动画
这个没有什么好说的啊,有需要那么看前面的核心动画。直接上代码。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MainAnnotation class]])
{
MainAnnotationView *mainAnnotationView = [MainAnnotationView mainAnnotationViewWithMapView:mapView andAnnotation:annotation];
return mainAnnotationView;
}
else if ([annotation isKindOfClass:[DescribeAnnotation class]])
{
DescribeAnnotationView *describeAnnotationView = [DescribeAnnotationView describeAnnotationViewWithMapView:mapView andAnnotation:annotation];
describeAnnotationView.delegate = self;
//显示标注视图的时候,执行动画。
[self implementCoreAnnimationWithView:describeAnnotationView.layer andAnnotation:annotation];
return describeAnnotationView;
}
else
{
return nil;
}
}
/**
* 执行核心动画
*
* @param layer 所需动画的CALayer
* @param annotation 为了确定动画位置所需的MKAnnotation对象
*/
- (void)implementCoreAnnimationWithView:(CALayer *)layer andAnnotation:(id<MKAnnotation>)annotation
{
//创建动画对象
CAKeyframeAnimation *animation = [[CAKeyframeAnimation alloc] init];
//设置动画属性
// animation.keyPath = @"transform.scale";
// animation.values = @[@(1),@(1.5),@(0.5),@(1.3),@(0.7),@(1.1),@(0.9),@(1)];
animation.keyPath = @"position.y";
CGFloat beginValue = [self.mapView convertCoordinate:annotation.coordinate toPointToView:self.mapView].y;
animation.values = @[@(beginValue),@(beginValue - 30),@(beginValue + 30),@(beginValue + 20),@(beginValue - 20),@(beginValue + 10),@(beginValue - 10),@(beginValue)];
animation.duration = 0.5;
animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
//为CALayer添加动画
[layer addAnimation:animation forKey:nil];
}
五.导航
1.导航需要使用MKDirections来计算路径,而MKDirections需要一个方向请求对象MKDirectionsRequest,MKDirectionsRequest设置好起点和终点。
2.计算完成之后会返回一个方向响应对象MKDirectionsResponse,此对象中一个路径(MKRoute)数组routes,然后在MapView中添加路径中的遮罩MKPolyline。
3.在MapView的代理方法- (MKOverlayRenderer )mapView:(MKMapView )mapView rendererForOverlay:(id)overlay中,我们需要返回一个遮罩的渲染器对象MKOverlayRenderer。此时只有返回它的子类MKPolylineRenderer才能设置颜色,才能可见。
//创建请求
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
//设置起点和终点
[request setSource:[[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithPlacemark:sourcePlacemark]]];
[request setDestination:[[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithPlacemark:self.endPlaceMark]]];
//创建方向对象
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
//计算路线
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[self showAlertControllerTitle:nil withMessge:@"对不起,没有为您找到合适的路线!" andCancelTitle:nil];
}
else
{
//添加两颗大头针
[self addAnnotation:sourcePlacemark.location.coordinate withTitle:@"出发地" andSubTitle:sourcePlacemark.name];
[self addAnnotation:self.endPlaceMark.location.coordinate withTitle:@"目的地" andSubTitle:self.endPlaceMark.name];
//先遍历所有的路线移除
for (id<MKOverlay> overlay in self.mapView.overlays) {
[self.mapView removeOverlay:overlay];
}
//遍历所有路线
for (MKRoute *route in response.routes) {
//添加遮罩
[self.mapView addOverlay:route.polyline];
}
//设置地图的显示范围
if (isChange) {
CLLocationCoordinate2D startCoordinate = sourcePlacemark.location.coordinate;
CLLocationCoordinate2D endCoordinate = self.endPlaceMark.location.coordinate;
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake((startCoordinate.latitude + endCoordinate.latitude)/2, (startCoordinate.longitude + endCoordinate.longitude)/2);
CGFloat proportion = (Screen_Height - 64 - 49 - 44)/Screen_Height;
CLLocationDegrees latitudeDelta = fabs(startCoordinate.latitude - endCoordinate.latitude)/proportion;
CLLocationDegrees longitudeDelta = fabs(startCoordinate.longitude - endCoordinate.longitude)/proportion;
MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
[self.mapView setRegion:MKCoordinateRegionMake(centerCoordinate, coordinateSpan) animated:YES];
}
}
}];
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.strokeColor = [UIColor greenColor];
return renderer;
}