2D地图SDK接入指南


在正式进行2D地图开发前请先参考工程设置配置您的工程,并在这里获取2D地图SDK

如需要了解2D地图SDK的基础类、方法和API,可查阅参考手册


Hello Map

在ViewController.h文件中添加QMapView。

#import <UIKit/UIKit.h>
#import <QMapKit/QMapKit.h>
 
@interface ViewController:UIViewController
@property (nonatomic, strong) QMapView *mapView;
@end

在ViewController.m文件添加实例化QMapView的代码。

self.mapView = [[QMapView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.mapView];

运行工程,地图显示效果如下:



  • Tips:

1、如果您感觉初始化后显示的地图锯齿太过明显可以按如下代码设置一下缩放级别

//这里的zoomlevel并没有使用整数,是因为地图SDK中的zoomlevel调整是左开右闭的。
//这里加了0.01,实际使用的底图是12级的
[self.mapView setZoomLevel:11.01];

2、如果您的系统版本高于iOS7.0,地图会根据按所在界面的status bar,navigationbar,与tabbar的高度,自动调整inset,可以在QmapView初始化之前添加如下设置

#define IOS7 [[[UIDevice currentDevice]systemVersion] floatValue] >= 7.0
 
if (IOS7)
{
    self.automaticallyAdjustsScrollViewInsets = NO;
}

地图设置

用户可以通过对QMapView对象设置地图的拖动、缩放及比例尺的显示和隐藏。示例代码如下

//初始化设置地图中心点坐标需要异步加入到主队列
dispatch_async(dispatch_get_main_queue(), ^{
    [_mapView setCenterCoordinate:CLLocationCoordinate2DMake(39.963438, 116.316376)
    zoomLevel:12.01 animated:NO];
});
//地图平移,默认YES
_mapView.scrollEnabled = YES;
//地图缩放,默认YES
_mapView.zoomEnabled = YES;
//比例尺是否显示,ƒF默认YES
_mapView.showsScale = YES;

地图状态

用户可以通过对QMapView对象获取地图的中心坐标、视图范围等信息。示例代码如下

//当前缩放级别
_mapView.zoomLevel;
//最小缩放级别
_mapView.minZoomLevel;
//最大缩放级别
_mapView.maxZoomLevel;
//中心点坐标
_mapView.centerCoordinate;
//当前视图范围
_mapView.region;

SDK还提供了mapView:regionWillChangeAnimated:和mapView:regionDidChangeAnimated:两个委托,方便用户在地图视图变化时实现自己的业务。示例代码如下:

- (void)mapView:(QMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
    NSLog(@"Region:\ncenter:[%f,%f]\nspan:[%f,%f]",
         _mapView.region.center.latitude,
         _mapView.region.center.longitude,
         _mapView.region.span.latitudeDelta,
         _mapView.region.span.longitudeDelta);
}
 
-(void)mapView:(QMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    NSLog(@"Region:\ncenter:[%f,%f]\nspan:[%f,%f]",
         _mapView.region.center.latitude,
         _mapView.region.center.longitude,
         _mapView.region.span.latitudeDelta,
         _mapView.region.span.longitudeDelta);
}

定位

SDK封装了系统定位,方便用户使用,在使用定位功能前,需要向info.plist文件中添加定位权限:(以下二选一,两个都添加默认使用NSLocationWhenInUseUsageDescription):


NSLocationWhenInUseUsageDescription:允许在前台使用时获取GPS的描述

NSLocationAlwaysUsageDescription:允许永久使用GPS的描述 这里还提供了SDK支持的定位相关的delegate。


下面的代码可以开启定位功能:
//QMapViewDelegate提供了取得定位状态及位置变化的方法
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.mapView = [[QMapView alloc] initWithFrame:self.view.bounds];
    self.mapView.delegate = self;
    [self.view addSubview:self.mapView];
    //开启定位功能
    [_mapView setShowsUserLocation:YES];
}
 
- (void)mapViewWillStartLocatingUser:(QMapView *)mapView
{
    //获取开始定位的状态
}
 
- (void)mapViewDidStopLocatingUser:(QMapView *)mapView
{
    //获取停止定位的状态
}
 
- (void)mapView:(QMapView *)mapView didUpdateUserLocation:(QUserLocation *)userLocation updatingLocation:(BOOL)updatingLocation
{
    //刷新位置
}

运行工程,定位效果截图如下:



标注

SDK 提供的地图标注为QPointAnnotation 类,不同的标记可以根据图标和改变信息窗的样式和内容加以区分。


1、添加pointAnnotation

下面是添加pointAnnotation的代码示例:

//定义pointAnnotation
QPointAnnotation *yinke = [[QPointAnnotation alloc] init];
yinke.title = @"银科";
yinke.coordinate = CLLocationCoordinate2DMake(39.982006,116.306333);
 
//向mapview添加annotation
[_mapView addAnnotation:yinke];

用户需要实现QMapViewDelegate中的mapView:viewForAnnotation:方法:

-(QAnnotationView *)mapView:(QMapView *)mapView
          viewForAnnotation:(id<QAnnotation>)annotation {
    static NSString *pointReuseIndentifier = @"pointReuseIdentifier";
 
    if ([annotation isKindOfClass:[QPointAnnotation class]]) {
        //添加默认pinAnnotation
        if ([annotation isEqual:[_annotations objectAtIndex:0]]) {
 
            QPinAnnotationView *annotationView = (QPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndentifier];
            if (annotationView == nil) {
                annotationView = [[QPinAnnotationView alloc]
                                  initWithAnnotation:annotation
                                  reuseIdentifier:pointReuseIndentifier];
            }
            //显示气泡,默认NO
            annotationView.canShowCallout = YES;
            //设置大头针颜色
            annotationView.pinColor = QPinAnnotationColorRed;
            //添加左侧信息窗附件
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
            annotationView.leftCalloutAccessoryView = btn;
            //可以拖动
            annotationView.draggable = YES;
            //自定义annotation图标
            //UIImage *image1 = [UIImage imageWithContentsOfFile:path1];
            return annotationView;
        }
    }
    return nil;
}

运行工程,annotation加载效果如下:



2、自定义annotation及callout

sdk提供的annotation能够满足大部分用户的需求,但有时候用户需要显示比较复杂的callout的时候,就需要用户自定义annotation及callout了。自定义annotation及callout的流程如下:


1、自定义CalloutView类

新建CustomCalloutView类,继承于UIView,头文件中添加如下定义:

//设置气泡title
-(void)setTitle:(NSString *)title;
//设置气泡subtitle
-(void)setSubtitle:(NSString *)subtitle;
//设置气泡显示的图片
-(void)setImage:(UIImage *)image;
//设置气泡button样式
-(void)setBtnTitle:(NSString *)text forState:(UIControlState)state;
//设置气泡button点击回调
-(void)btnAddTarget:(id)target
             action:(SEL)action
   forControlEvents:(UIControlEvents)evnets;

在实现文件中绘制view,实现代码如下:

#define arrowHeight 10
 
#define portraitMargin 5
#define portraitWidth 50
#define portraitHeight 70
 
#define titleWidth 120
#define titleHeight 20
 
#define btnWidth 50
#define btnHeight 30
 
- (void)drawRect:(CGRect)rect {
    [self drawInContext:UIGraphicsGetCurrentContext()];
    self.layer.shadowColor = [UIColor blackColor].CGColor;
    self.layer.shadowOpacity = 1.0;
    self.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
}
 
- (void)drawInContext:(CGContextRef) context {
    CGContextSetLineWidth(context, 2.0);
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    [self getDarwPath:context];
    CGContextFillPath(context);
}
 
- (void)getDarwPath:(CGContextRef) context {
    CGRect rect = self.bounds;
    CGFloat radius = 6.0;
    CGFloat minx = CGRectGetMinX(rect),
    midx = CGRectGetMidX(rect),
    maxx = CGRectGetMaxX(rect);
    CGFloat miny = CGRectGetMinY(rect),
    maxy = CGRectGetMaxY(rect) - arrowHeight;
    //绘制三角形
    CGContextMoveToPoint(context, midx + arrowHeight, maxy);
    CGContextAddLineToPoint(context, midx, maxy + arrowHeight);
    CGContextAddLineToPoint(context, midx - arrowHeight, maxy);
    //绘制圆角矩形
    CGContextAddArcToPoint(context, minx, maxy, minx, miny, radius);
    CGContextAddArcToPoint(context, minx, miny, maxx, miny, radius);
    CGContextAddArcToPoint(context, maxx, miny, maxx, maxy, radius);
    CGContextAddArcToPoint(context, maxx, maxy, minx, maxy, radius);
 
    CGContextClosePath(context);
}

重写initWithFrame:frame初始化气泡,实现代码如下:

@interface CustomCalloutView()
 
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIButton *btn;
@property (nonatomic, strong) UILabel *labelTitle;
@property (nonatomic, strong) UILabel *labelSubtitle;
@property (nonatomic, strong) UIStackView *stackView;
 
@end
 
-(instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        [self initSubView];
    }
    return self;
}
 
- (void)initSubView {
    _imageView = [[UIImageView alloc]
                  initWithFrame:CGRectMake(portraitMargin,
                                           portraitMargin,
                                           portraitWidth,
                                           portraitHeight)];
    [self addSubview:_imageView];
 
    _stackView = [[UIStackView alloc]
                  initWithFrame:CGRectMake(portraitMargin *2 + portraitWidth,
                                           portraitMargin,
                                           titleWidth + portraitMargin * 2,
                                           portraitHeight + portraitMargin)];
    _stackView.axis = UILayoutConstraintAxisVertical;
    _stackView.alignment = UIStackViewAlignmentLeading;
    [self addSubview:_stackView];
    //添加title
    _labelTitle = [[UILabel alloc] initWithFrame:CGRectMake(0,
                                                            0,
                                                            titleWidth,
                                                            titleHeight)];
    [_stackView addArrangedSubview:_labelTitle];
    //添加subtitle
    _labelSubtitle = [[UILabel alloc] initWithFrame:CGRectMake(0,
                                                               0,
                                                               titleWidth,
                                                               titleHeight)];
    [_stackView addArrangedSubview:_labelSubtitle];
    //添加button
    _btn = [UIButton buttonWithType:UIButtonTypeSystem];
    _btn.frame = CGRectMake(0, 0, btnWidth, btnHeight);
    [_stackView addArrangedSubview:_btn];
}

气泡中各控件的实现代码如下:

-(void)setTitle:(NSString *)title {
    _labelTitle.text = title;
}
 
-(void)setSubtitle:(NSString *)subtitle {
    _labelSubtitle.text = subtitle;
}
 
-(void)setImage:(UIImage *)image {
    _imageView.image = image;
}
 
-(void)setBtnTitle:(NSString *)text forState:(UIControlState)state {
    if (text == nil) {
        return;
    }
    text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if (text.length == 0) {
        return;
    }
    [_btn setTitle:text forState:state];
}
 
-(void)btnAddTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents {
    if (_btn != nil) {
        [_btn addTarget:target action:action forControlEvents:controlEvents];
    }
}

2、自定义AnnotationView

新建CustomAnnotationView类,继承于QAnnotationView,头文件中添加如下定义:

//设置callout要显示的图片
-(void)setCalloutImage:(UIImage *)image;
 
//设置callout内置button的标题和状态
-(void)setCalloutBtnTitle:(NSString *)title
                 forState:(UIControlState)state;
 
//设置callout的button回调
-(void)addCalloutBtnTarget:(id)target
                       action:(SEL)action
             forControlEvents:(UIControlEvents)events;

在实现文件中添加如下属性,及实现头文件中的声明方法:

@property (nonatomic, strong) UIImage *imageCallout;
@property (nonatomic, strong) NSString *btnTitle;
@property (nonatomic, strong) id btnTarget;
@property (nonatomic) UIControlState btnState;
@property (nonatomic) SEL btnAction;
@property (nonatomic) UIControlEvents btnControlEvents;
@property (nonatomic, strong) CustomCalloutView *customCalloutView;
 
-(void)setCalloutImage:(UIImage *)image {
    _imageCallout = image;
}
 
-(void)setCalloutBtnTitle:(NSString *)title
                 forState:(UIControlState)state {
    _btnTitle = title;
}
 
-(void)addCalloutBtnTarget:(id)target
                       action:(SEL)action
             forControlEvents:(UIControlEvents)events {
    _btnTarget = target;
    _btnAction = action;
    _btnControlEvents = events;
}

弹出callout需要重写setSelected:selected及setSelected:animated:,实现代码如下:

-(void)setSelected:(BOOL)selected {
    [self setSelected:selected animated:NO];
}
 
-(void)setSelected:(BOOL)selected animated:(BOOL)animated {
    if (self.selected == selected) {
        return;
    }
    if (selected) {
        //需要弹出callout的时候才开始初始化CalloutView
        if (_customCalloutView == nil) {
            _customCalloutView = [[CustomCalloutView alloc]
                                  initWithFrame:CGRectMake(0,
                                                           0,
                                                           calloutWidth,
                                                           calloutHeight)];
            _customCalloutView.center =
                    CGPointMake(CGRectGetWidth(self.bounds) / 2,
                                -CGRectGetHeight(self.customCalloutView.bounds)/2);
            [_customCalloutView setTitle:self.annotation.title];
            [_customCalloutView setSubtitle:self.annotation.subtitle];
            [_customCalloutView setImage:_imageCallout];
            [_customCalloutView setBtnTitle:_btnTitle
                                   forState:_btnState];
            [_customCalloutView btnAddTarget:_btnTarget
                                      action:_btnAction
                            forControlEvents:_btnControlEvents];
        }
        [self addSubview:_customCalloutView];
    } else {
        [_customCalloutView removeFromSuperview];
    }
 
    [super setSelected:selected animated:animated];
}

要想实现CalloutView的可点击,需要重写pointInside:withEvent:,具体实现如下:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    //sdk内的annotation点击到图标返回YES,否则NO
    BOOL inside = [super pointInside:point withEvent:event];
 
    if (!inside)
    {
        /* Callout existed. */
        if (self.selected && self.customCalloutView.superview)
        {
            CGPoint pointInCallout = [self convertPoint:point toView:self.customCalloutView];
 
            inside = CGRectContainsPoint(self.customCalloutView.bounds, pointInCallout);
        }
    }
 
    return inside;
}

3、向地图添加annotation

自定义annotation的准备工作已经完成,下面需要在mapView:viewForAnnotation:中添加自定义annotation:

-(QAnnotationView *)mapView:(QMapView *)mapView
          viewForAnnotation:(id<QAnnotation>)annotation {
    static NSString *customReuseIndentifier = @"custReuseIdentifieer";
 
    if ([annotation isKindOfClass:[QPointAnnotation class]]) {
        //添加自定义annotation
        if ([annotation isEqual:[_annotations objectAtIndex:2]]) {
            CustomAnnotationView *annotationView = (CustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:customReuseIndentifier];
            if (annotationView == nil) {
                annotationView = [[CustomAnnotationView alloc]
                                  initWithAnnotation:annotation
                                  reuseIdentifier:customReuseIndentifier];
                //自定义的annotation图标
                NSString *path = [[NSBundle mainBundle]
                                  pathForResource:@"redflag"
                                  ofType:@"png"];
                UIImage *image = [UIImage imageWithContentsOfFile:path];
                annotationView.image = image;
                annotationView.centerOffset = CGPointMake(image.size.width / 2, - image.size.height / 2);
                //自定义的callout中的图标
                NSString *path1 = [[NSBundle mainBundle]
                                  pathForResource:@"tiananmen"
                                  ofType:@"png"];
                UIImage *image1 = [UIImage imageWithContentsOfFile:path1];
                [annotationView setCalloutImage:image1];
                [annotationView setCalloutBtnTitle:@"到这里去"
                                          forState:UIControlStateNormal];
                [annotationView addCalloutBtnTarget:self
                                             action:@selector(calloutButtonAction)
                                   forControlEvents:UIControlEventTouchUpInside];
                return annotationView;
            }
        }
    }
    return nil;
}

自定义的callout效果截图如下:



图形绘制

1、折线

折线是由Polyline类定义的一组在地图上相连的线段,它由一组LatLng点按照一定顺序连接而成。QPolyline对象的初始化方法如下:

CLLocationCoordinate2D lineCoords[4];
lineCoords[0] = CLLocationCoordinate2DMake(39.980672,116.308651);
lineCoords[1] = CLLocationCoordinate2DMake(39.968964,116.344185);
lineCoords[2] = CLLocationCoordinate2DMake(39.937964,116.314185);
lineCoords[3] = CLLocationCoordinate2DMake(39.915266,116.324615);
QPolyline *polyLine = [QPolyline polylineWithCoordinates:lineCoords count:4];
//向地图添加折线
[_mapView addOverlay:polyLine];

还需要在mapView:viewForOverlay:中添加PolylineView将polyline显示到地图上:

-(QOverlayView *)mapView:(QMapView *)mapView
          viewForOverlay:(id<QOverlay>)overlay {
    if ([overlay isKindOfClass:[QPolyline class]]) {
        QPolylineView *polylineView = [[QPolylineView alloc] initWithPolyline:overlay];
        polylineView.lineWidth = 5.0f;
        polylineView.strokeColor = [UIColor redColor];
        //虚线
        polylineView.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:8] , [NSNumber numberWithInt:8], nil];
        return polylineView;
    }
    return nil;
}

截图如下:



2、多边形

多边形是由Polygon类定义的一组在地图上的封闭线段组成的图形,它由一组LatLng点按顺序连接而成的封闭图形。QPolygon的初始化方法如下:

CLLocationCoordinate2D polygonPeak[3];
polygonPeak[0] = CLLocationCoordinate2DMake(39.969753,116.419373);
polygonPeak[1] = CLLocationCoordinate2DMake(39.935013,116.472932);
polygonPeak[2] = CLLocationCoordinate2DMake(39.890772,116.358948);
QPolygon *polygon = [QPolygon polygonWithCoordinates:polygonPeak count:3];
//向地图添加折线
[_mapView addOverlay:polygon];

还需要在mapView:viewForOverlay:中添加PolygonView将polyline显示到地图上:

-(QOverlayView *)mapView:(QMapView *)mapView
          viewForOverlay:(id<QOverlay>)overlay {
    if ([overlay isKindOfClass:[QPolygon class]]) {
        QPolygonView *polygonView = [[QPolygonView alloc] initWithPolygon:overlay];
        polygonView.strokeColor = [UIColor colorWithRed:1.0f
                                                  green:0.0f
                                                   blue:0.0f
                                                  alpha:0.5f];
        polygonView.fillColor = [UIColor colorWithRed:0.0f
                                                green:1.0f
                                                 blue:0.0f
                                                alpha:0.3f];
        polygonView.lineWidth = 5.0f;
        return polygonView;
    }
    return nil;
}

截图如下:



3、圆

圆形是由Circle类定义的封闭曲线,在腾讯地图构造一个圆形需要确定它的圆心和半径。

QCircle *circle = [QCircle
                       circleWithCenterCoordinate:CLLocationCoordinate2DMake(39.870749,116.445466)
                       radius:3000];
//向地图添加折线
[_mapView addOverlay:polygon];

还需要在mapView:viewForOverlay:中添加CircleView将polyline显示到地图上:

-(QOverlayView *)mapView:(QMapView *)mapView
          viewForOverlay:(id<QOverlay>)overlay {
    if ([overlay isKindOfClass:[QCircle class]]) {
        QCircleView *circleView = [[QCircleView alloc] initWithCircle:overlay];
        circleView.strokeColor = [UIColor colorWithRed:1.0f
                                                 green:0.0f
                                                  blue:0.0f
                                                 alpha:0.5f];
        circleView.fillColor = [UIColor colorWithRed:0.0f
                                               green:0.0f
                                                blue:0.5f
                                               alpha:0.3f];
        circleView.lineWidth = 5.0f;
        return circleView;
    }
    return nil;
}

截图如下:



回调接口

SDK的QMapViewDelegate protocol提供了丰富的回调接口,方便用户实现自己的业务,具体的回调列表如下:

接口 说明
mapViewDidFailLoadingMap:withError: 地图数据加载失败时会调用此接口
mapView:regionWillChangeAnimated: 地图区域即将改变时会调用此接口
mapView:regionDidChangeAnimated: 地图区域改变完成时会调用此接口
mapView:viewForOverlay: 根据overlay生成对应的view
mapView:didAddOverlayViews: 当mapView新添加overlayViews时,调用此接口
mapView:viewForAnnotation: 根据annotation生成对应的view
mapView:didAddAnnotationViews: 当mapView新添加annotationViews时,调用此接口
mapView:didSelectAnnotationView: 当选中一个annotationView时,调用此接口
mapView:didDeselectAnnotationView: 当取消选中一个annotationView时,调用此接口
mapView:annotationView: 拖动annotationView时view的状态变化,ios3.2以后支持
mapView:annotationView:calloutAccessoryControlTapped: 标注view的accessory view(必须继承自UIControl)被点击时,触发该回调
mapViewWillStartLocatingUser: 在地图view将要启动定位时,会调用此函数
mapViewDidStopLocatingUser: 在地图view定位停止后,会调用此函数
mapView:didUpdateUserLocation:updatingLocation: 位置或者设备方向更新后,会调用此函数
mapView:didFailToLocateUserWithError: 定位失败后,会调用此函数
mapView:didChangeUserTrackingMode:animated: 当userTrackingMode改变时,调用此接口

如果您想实现地图的点击事件,需要自己向地图添加手势识别,下面是示例代码:

首先继承UIGestureRecognizerDelegate协议,并实现下列方法:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
//点击地图时的回调
-(void)gestureAction:(UIGestureRecognizer *)gestureRecognizer {
    CGPoint point = [gestureRecognizer locationOfTouch:0 inView:_mapView];
    NSLog(@"Tap at:%f,%f", point.x, point.y);
}

向地图添加点击手势:

//添加手势识别
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc]
                                         initWithTarget:self
                                         action:@selector(gestureAction:)];
[gestureRecognizer setDelegate:self];
[_mapView addGestureRecognizer:gestureRecognizer];

计算工具

SDK提供了丰富的计算工具,方便开发者获取两点距离、点与平面图形的关系等,QGeometry头文件提供了详细的接口说明。这里仅举例说明举两点间的距离计算:

//计算两点间距离,先将经纬度坐标转换为QMapPoint,然后计算两个QMapPoint间的距离
QMapPoint point1 = QMapPointForCoordinate(CLLocationCoordinate2DMake(39.960280,116.356202));
QMapPoint point2 = QMapPointForCoordinate(CLLocationCoordinate2DMake(39.953964,116.460572));
double distance = QMetersBetweenMapPoints(point1, point2);

动画移动 annotation

在某些场景下,用户需要动画移动 annotation 时,可以下载 腾讯2D地图工具包可以方便的为 annotation 添加平移动画。下面是为 annotation 添加动画的示例,更多配置可以参考 demo 及压缩包中提供的文档。

- (void)testAnnotationKeyframeAnimationWithAnnotation:(id <QAnnotation>)annotation
                                              mapView:(QMapView *)mapView
{
    if (annotation == nil || mapView == nil)
    {
        return;
    }
 
    QAnnotationView *annotationView = [mapView viewForAnnotation:annotation];
 
    if (annotationView == nil)
    {
        return;
    }
 
    // 地图右中
    CGPoint p1 = CGPointMake(CGRectGetMaxX(self.mapView.bounds), CGRectGetMidY(mapView.bounds));
    CLLocationCoordinate2D c1 = [self.mapView convertPoint:p1 toCoordinateFromView:mapView];
 
    // 地图下中
    CGPoint p2 = CGPointMake(CGRectGetMidX(self.mapView.bounds), CGRectGetMaxY(mapView.bounds));
    CLLocationCoordinate2D c2 = [self.mapView convertPoint:p2 toCoordinateFromView:mapView];
 
    NSMutableArray *coordinates = [NSMutableArray array];
    [coordinates addObject:[NSValue valueWithCoordinate:c1]];
    [coordinates addObject:[NSValue valueWithCoordinate:c2]];
 
    [annotationView animationWithDuration:5
                                    delay:0
                              coordinates:coordinates
                               completion:^(BOOL finished) {
                                   NSLog(@"keyframe animation finished = %d", finished);
 
                                   CLLocationCoordinate2D c1 = CLLocationCoordinate2DMake(39.4, 116.4);
 
                                   CLLocationCoordinate2D c2 = CLLocationCoordinate2DMake(39.1, 116.1);
 
                                   NSMutableArray *coordinates = [NSMutableArray array];
                                   [coordinates addObject:[NSValue valueWithCoordinate:c1]];
                                   [coordinates addObject:[NSValue valueWithCoordinate:c2]];
                                   [annotationView animationWithDuration:5
                                                                   delay:0
                                                             coordinates:coordinates
                                                              completion:nil];
                               }];
}

这篇文章对您解决问题是否有帮助?

已解决
未解决