iOS-百度地图添加标注自定义气泡

时间:2021-08-08 11:49:58

原地址:http://www.jianshu.com/p/6a334f071c69


文/煜寒了(简书作者)
原文链接:http://www.jianshu.com/p/6a334f071c69
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

1.首先实现添加多个标注和自定义气泡

添加自定义标注

[_mapView addAnnotations:array];

arry 中放入标注(BMKPointAnnotation)的数组,此方法添加多个标注。

当添加多个标注时就触发以下代理方法

#pragma mark -- BMKMapdelegate

/** *根据anntation生成对应的View *@param mapView 地图View *@param annotation 指定的标注 *@return 生成的标注View */
-(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id <BMKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[BMKPointAnnotation class]]) {
        BMKPinAnnotationView *newAnnotationView = [[BMKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:@"myAnnotation"];
        newAnnotationView.animatesDrop = YES;
        newAnnotationView.annotation = annotation;
        //这里我根据自己需要,继承了BMKPointAnnotation,添加了标注的类型等需要的信息
        MyBMKPointAnnotation *tt = (MyBMKPointAnnotation *)annotation;

        //判断类别,需要添加不同类别,来赋予不同的标注图片
        if (tt.profNumber == 100000) {
            newAnnotationView.image = [UIImage imageNamed:@"ic_map_mode_category_merchants_normal.png"];
        }else if (tt.profNumber == 100001){

        }
        //设定popView的高度,根据是否含有缩略图
        double popViewH = 60;
        if (annotation.subtitle == nil) {
            popViewH = 38;
        }
        UIView *popView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth-100, popViewH)];
        popView.backgroundColor = [UIColor whiteColor];
        [popView.layer setMasksToBounds:YES];
        [popView.layer setCornerRadius:3.0];
        popView.alpha = 0.9;
// //设置弹出气泡图片
// UIImageView *image = [[UIImageView alloc]initWithImage:[UIImage imageNamed:tt.imgPath]];
// image.frame = CGRectMake(0, 160, 50, 60);
// [popView addSubview:image];

        //自定义气泡的内容,添加子控件在popView上
        UILabel *driverName = [[UILabel alloc]initWithFrame:CGRectMake(8, 4, 160, 30)];
        driverName.text = annotation.title;
        driverName.numberOfLines = 0;
        driverName.backgroundColor = [UIColor clearColor];
        driverName.font = [UIFont systemFontOfSize:15];
        driverName.textColor = [UIColor blackColor];
        driverName.textAlignment = NSTextAlignmentLeft;
        [popView addSubview:driverName];

        UILabel *carName = [[UILabel alloc]initWithFrame:CGRectMake(8, 30, 180, 30)];
        carName.text = annotation.subtitle;
        carName.backgroundColor = [UIColor clearColor];
        carName.font = [UIFont systemFontOfSize:11];
        carName.textColor = [UIColor lightGrayColor];
        carName.textAlignment = NSTextAlignmentLeft;
        [popView addSubview:carName];

        if (annotation.subtitle != nil) {
        UIButton *searchBn = [[UIButton alloc]initWithFrame:CGRectMake(170, 0, 50, 60)];
        [searchBn setTitle:@"查看路线" forState:UIControlStateNormal];
        searchBn.backgroundColor = mainColor;
            searchBn.titleLabel.numberOfLines = 0;
        [searchBn addTarget:self action:@selector(searchLine)];
        [popView addSubview:searchBn];
        }

        BMKActionPaopaoView *pView = [[BMKActionPaopaoView alloc]initWithCustomView:popView];
        pView.frame = CGRectMake(0, 0, ScreenWidth-100, popViewH);
        ((BMKPinAnnotationView*)newAnnotationView).paopaoView = nil;
        ((BMKPinAnnotationView*)newAnnotationView).paopaoView = pView;
        return newAnnotationView;
    }
    return nil;
}

点击标注和气泡响应方法

/** * 当选中一个annotation views时,调用此接口 * @param mapView 地图View * @param views 选中的annotation views */
- (void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view
{
    _shopCoor = view.annotation.coordinate;
}
/** * 选中气泡调用方法 * @param mapView 地图 * @param view annotation */
- (void)mapView:(BMKMapView *)mapView annotationViewForBubble:(BMKAnnotationView *)view
{
    MyBMKPointAnnotation *tt = (MyBMKPointAnnotation *)view.annotation;
    if (tt.shopID) {
        BusinessIfonUVC *BusinessIfonVC = [[BusinessIfonUVC alloc]init];
        BusinessIfonVC.shopId = tt.shopID;
        [self.navigationController pushViewController:BusinessIfonVC animated:YES];
    }
}

2.实现路线搜索,路径规划,获取街道名称等功能

通过经纬度获取地址,逆地理编码

-(void)getStartAddress
{
    //起点地址
    CLGeocoder *Geocoder = [[CLGeocoder alloc]init];
    CLGeocodeCompletionHandler handler = ^(NSArray *place,NSError *error){
        for(CLPlacemark *placemark in place){
            NSString *tmp = [[NSString alloc]init];
            tmp = placemark.subThoroughfare;
            if (tmp == nil) {
                tmp = @"";
            }
            NSString *startAdr = [[NSString alloc]initWithFormat:@"%@%@",placemark.thoroughfare,tmp];
            _startCityText.text = placemark.locality;
            if ([startAdr isEqualToString:@"(null)"]) {
                _startAddrText.text = @"获取地址失败";
            }else{
            _startAddrText.text = startAdr;
            }
    }
    };
    CLLocation *loc = [[CLLocation alloc]initWithLatitude:self.startCoor.latitude longitude:self.startCoor.longitude];
    [Geocoder reverseGeocodeLocation:loc completionHandler:handler];

}

路径检索,该部分没有整理,将乘车和换乘信息放到了LineInfo,steps等模型中。

- (void)onGetTransitRouteResult:(BMKRouteSearch*)searcher result:(BMKTransitRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    NSMutableArray *lineArr = [[NSMutableArray alloc]init];

    NSArray* array = [NSArray arrayWithArray:_mapView.annotations];
    [_mapView removeAnnotations:array];
    array = [NSArray arrayWithArray:_mapView.overlays];
    [_mapView removeOverlays:array];
    if (error == BMK_SEARCH_NO_ERROR) {
        for(int j = 0; j < [result.routes count];j++)
        {

        NSMutableArray *busTitleArr = [[NSMutableArray alloc]init];
        NSMutableArray *lineStepsArr = [[NSMutableArray alloc]init];
        NSMutableArray *stepsArr = [[NSMutableArray alloc]init];
    //生成数据模型
        LineInfoModel *lineInfo = [[LineInfoModel alloc]init];
        BMKTransitRouteLine* plan = (BMKTransitRouteLine*)[result.routes objectAtIndex:j];
    //数据模型:获得路线长度
        lineInfo.distance = plan.distance;
    //数据模型:获得路线消耗时间
        lineInfo.dates = plan.duration.dates;
        lineInfo.hours = plan.duration.hours;
        lineInfo.minutes = plan.duration.minutes;
        lineInfo.seconds = plan.duration.seconds;
    // 获得轨迹点
        lineInfo.planStepsArr = plan.steps;
        // 计算路线方案中的路段数目
        int size = [plan.steps count];
        int planPointCounts = 0;

        for (int i = 0; i < size; i++) {
            BMKTransitStep* transitStep = [plan.steps objectAtIndex:i];

    //数据模型:获得乘坐公交数组
            DLog(@"%@",transitStep.vehicleInfo.title);
            if (transitStep.vehicleInfo.title) {
                [busTitleArr addObject:transitStep.vehicleInfo.title];
            }
    //数据模型:获取换乘信息
            if (transitStep.instruction) {
                transitStep.instruction = [transitStep.instruction stringByReplacingOccurrencesOfString:@"<font color=\"#313233\">" withString:@""];
                [lineStepsArr addObject:transitStep.instruction];
            }
            DLog(@"%@ %@",transitStep.vehicleInfo.title,transitStep.instruction);

            if(i==0){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.starting.location;
                item.title = @"起点";
                item.type = 0;
// [_mapView addAnnotation:item]; // 添加起点标注
// 
//
                [stepsArr addObject:item];
            }else if(i==size-1){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.terminal.location;
                item.title = @"终点";
                item.type = 1;
                [stepsArr addObject:item];
// [_mapView addAnnotation:item]; // 添加起点标注
// 
            }
            RouteAnnotation* item = [[RouteAnnotation alloc]init];
            item.coordinate = transitStep.entrace.location;
            item.title = transitStep.instruction;
            transitStep.instruction = [transitStep.instruction stringByReplacingOccurrencesOfString:@"<font color=\"#313233\">" withString:@""];

            item.type = 3;
            [stepsArr addObject:item];
// [_mapView addAnnotation:item];
// 
// 
// //轨迹点总数累计
            planPointCounts += transitStep.pointsCount;
        }
        lineInfo.vehicleInfoArr = busTitleArr;
        lineInfo.lineStepsArr = lineStepsArr;
        lineInfo.stepsArr = stepsArr;
        lineInfo.planPointCounts = planPointCounts;

        [lineArr addObject:lineInfo];

// //轨迹点
// BMKMapPoint * temppoints = new BMKMapPoint[planPointCounts];
// int i = 0;
// for (int j = 0; j < size; j++) {
// BMKTransitStep* transitStep = [plan.steps objectAtIndex:j];
// int k=0;
// for(k=0;k<transitStep.pointsCount;k++) {
// temppoints[i].x = transitStep.points[k].x;
// temppoints[i].y = transitStep.points[k].y;
// i++;
// }

        }
        self.lineStatusArr = lineArr;
      // 通过points构建BMKPolyline
// BMKPolyline* polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
// [_mapView addOverlay:polyLine]; // 添加路线overlay
// delete []temppoints;
// }
        [_tableView reloadData];

    }

}
- (void)onGetDrivingRouteResult:(BMKRouteSearch*)searcher result:(BMKDrivingRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    NSArray* array = [NSArray arrayWithArray:_mapView.annotations];
    [_mapView removeAnnotations:array];
    array = [NSArray arrayWithArray:_mapView.overlays];
    [_mapView removeOverlays:array];
    if (error == BMK_SEARCH_NO_ERROR) {
        BMKDrivingRouteLine* plan = (BMKDrivingRouteLine*)[result.routes objectAtIndex:0];
        // 计算路线方案中的路段数目
        int size = [plan.steps count];
        int planPointCounts = 0;
        for (int i = 0; i < size; i++) {
            BMKDrivingStep* transitStep = [plan.steps objectAtIndex:i];
            if(i==0){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.starting.location;
                item.title = @"起点";
                item.type = 0;
                [_mapView addAnnotation:item]; // 添加起点标注


            }else if(i==size-1){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.terminal.location;
                item.title = @"终点";
                item.type = 1;
                [_mapView addAnnotation:item]; // 添加起点标注

            }
            //添加annotation节点
            RouteAnnotation* item = [[RouteAnnotation alloc]init];
            item.coordinate = transitStep.entrace.location;
            item.title = transitStep.entraceInstruction;
            item.degree = transitStep.direction * 30;
            item.type = 4;
            [_mapView addAnnotation:item];

            //轨迹点总数累计
            planPointCounts += transitStep.pointsCount;
        }
        // 添加途经点
        if (plan.wayPoints) {
            for (BMKPlanNode* tempNode in plan.wayPoints) {
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item = [[RouteAnnotation alloc]init];
                item.coordinate = tempNode.pt;
                item.type = 5;
                item.title = tempNode.name;
                [_mapView addAnnotation:item];

            }
        }
        //轨迹点
        BMKMapPoint * temppoints = new BMKMapPoint[planPointCounts];
        int i = 0;
        for (int j = 0; j < size; j++) {
            BMKDrivingStep* transitStep = [plan.steps objectAtIndex:j];
            int k=0;
            for(k=0;k<transitStep.pointsCount;k++) {
                temppoints[i].x = transitStep.points[k].x;
                temppoints[i].y = transitStep.points[k].y;
                i++;
            }

        }
        // 通过points构建BMKPolyline
        BMKPolyline* polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
        [_mapView addOverlay:polyLine]; // 添加路线overlay
        delete []temppoints;


    }
}

- (void)onGetWalkingRouteResult:(BMKRouteSearch*)searcher result:(BMKWalkingRouteResult*)result errorCode:(BMKSearchErrorCode)error
{
    NSArray* array = [NSArray arrayWithArray:_mapView.annotations];
    [_mapView removeAnnotations:array];
    array = [NSArray arrayWithArray:_mapView.overlays];
    [_mapView removeOverlays:array];
    if (error == BMK_SEARCH_NO_ERROR) {
        BMKWalkingRouteLine* plan = (BMKWalkingRouteLine*)[result.routes objectAtIndex:0];
        int size = [plan.steps count];
        int planPointCounts = 0;
        for (int i = 0; i < size; i++) {
            BMKWalkingStep* transitStep = [plan.steps objectAtIndex:i];
            if(i==0){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.starting.location;
                item.title = @"起点";
                item.type = 0;
                [_mapView addAnnotation:item]; // 添加起点标注


            }else if(i==size-1){
                RouteAnnotation* item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.terminal.location;
                item.title = @"终点";
                item.type = 1;
                [_mapView addAnnotation:item]; // 添加起点标注

            }
            //添加annotation节点
            RouteAnnotation* item = [[RouteAnnotation alloc]init];
            item.coordinate = transitStep.entrace.location;
            item.title = transitStep.entraceInstruction;
            item.degree = transitStep.direction * 30;
            item.type = 4;
            [_mapView addAnnotation:item];

            //轨迹点总数累计
            planPointCounts += transitStep.pointsCount;
        }

        //轨迹点
        BMKMapPoint * temppoints = new BMKMapPoint[planPointCounts];
        int i = 0;
        for (int j = 0; j < size; j++) {
            BMKWalkingStep* transitStep = [plan.steps objectAtIndex:j];
            int k=0;
            for(k=0;k<transitStep.pointsCount;k++) {
                temppoints[i].x = transitStep.points[k].x;
                temppoints[i].y = transitStep.points[k].y;
                i++;
            }

        }
        // 通过points构建BMKPolyline
        BMKPolyline* polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
        [_mapView addOverlay:polyLine]; // 添加路线overlay
        delete []temppoints;

    }

}

3.画路径

我这里实现是跳转到另一个控制器中了,下面是他一些需要的数据

//路线长度
@property (nonatomic,assign) int distance;
//路线消耗时间
@property (nonatomic,assign) int dates;
@property (nonatomic,assign) int hours;
@property (nonatomic,assign) int minutes;
@property (nonatomic,assign) int seconds;
//交通工具数组
@property (nonatomic,strong) NSArray *vehicleInfoArr;
//换乘信息
@property (nonatomic,strong) NSArray *lineStepsArr;
//节点
@property (nonatomic,strong) NSArray *stepsArr;
//轨迹点个数
@property (nonatomic,assign) int planPointCounts;
//轨迹点
@property (nonatomic,strong) NSArray *planStepsArr;

接下来是画路经,关于乘车数据的展示,就是一个tableview上添加了手势,不做解释。

-(void)drawMap
{
    BMKPointAnnotation* item = [[BMKPointAnnotation alloc]init];
    item = [_lineInfo.stepsArr firstObject];
    [_mapView setCenterCoordinate:item.coordinate];
    [_mapView addAnnotations:_lineInfo.stepsArr];

    BMKMapPoint* temppoints = (BMKMapPoint *)malloc(sizeof(CLLocationCoordinate2D) * _lineInfo.planPointCounts);
    int i = 0;
    for (int j = 0; j < [_lineInfo.planStepsArr count]; j++) {
            BMKTransitStep* transitStep = [_lineInfo.planStepsArr objectAtIndex:j];
            int k=0;
            for(k=0;k<transitStep.pointsCount;k++) {
                temppoints[i].x = transitStep.points[k].x;
                temppoints[i].y = transitStep.points[k].y;
                i++;

        }

    }

    BMKPolyline* polyLine =[BMKPolyline  polylineWithPoints:temppoints count:_lineInfo.planPointCounts];
    if (nil != polyLine) {
           [_mapView addOverlay:polyLine]; // 添加路线overlay
    }
    free(temppoints);
}

- (BMKOverlayView*)mapView:(BMKMapView *)map viewForOverlay:(id<BMKOverlay>)overlay
{
    if ([overlay isKindOfClass:[BMKPolyline class]]) {
        BMKPolylineView* polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        polylineView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:1];
        polylineView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        polylineView.lineWidth = 3.0;
        return polylineView;
    }
    return nil;
}
// 判断标注类型,来处理
- (BMKAnnotationView*)getRouteAnnotationView:(BMKMapView *)mapview viewForAnnotation:(MyBMKPointAnnotation*)routeAnnotation
{
    BMKAnnotationView* view = nil;
    switch (routeAnnotation.type) {
        case 0:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"start_node"];
                view.image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_start.png"]];
                view.centerOffset = CGPointMake(0, -(view.frame.size.height * 0.5));
                view.canShowCallout = TRUE;
            }
            view.annotation = routeAnnotation;
        }
            break;
        case 1:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"end_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"end_node"];
                view.image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_end.png"]];
                view.centerOffset = CGPointMake(0, -(view.frame.size.height * 0.5));
                view.canShowCallout = TRUE;
            }
            view.annotation = routeAnnotation;
        }
            break;
        case 2:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"bus_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"bus_node"];
                view.image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_bus.png"]];
                view.canShowCallout = TRUE;
            }
            view.annotation = routeAnnotation;
        }
            break;
        case 3:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"rail_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"rail_node"];
                view.image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_rail.png"]];
                view.canShowCallout = TRUE;
            }
            view.annotation = routeAnnotation;
        }
            break;
        case 4:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"route_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"route_node"];
                view.canShowCallout = TRUE;
            } else {
                [view setNeedsDisplay];
            }

            UIImage* image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_direction.png"]];
            view.image = [image imageRotatedByDegrees:routeAnnotation.degree];
            view.annotation = routeAnnotation;

        }
            break;
        case 5:
        {
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"waypoint_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"waypoint_node"];
                view.canShowCallout = TRUE;
            } else {
                [view setNeedsDisplay];
            }

            UIImage* image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_waypoint.png"]];
            view.image = [image imageRotatedByDegrees:routeAnnotation.degree];
            view.annotation = routeAnnotation;
        }
            break;
        default:
            break;
    }

    return view;
}

- (BMKAnnotationView *)mapView:(BMKMapView *)view viewForAnnotation:(id <BMKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[BMKPointAnnotation class]]) {
        return [self getRouteAnnotationView:view viewForAnnotation:(MyBMKPointAnnotation *)annotation];
    }
    return nil;
}

- (UIImage*)imageRotatedByDegrees:(CGFloat)degrees
{

    CGFloat width = CGImageGetWidth(self.CGImage);
    CGFloat height = CGImageGetHeight(self.CGImage);

    CGSize rotatedSize;

    rotatedSize.width = width;
    rotatedSize.height = height;

    UIGraphicsBeginImageContext(rotatedSize);
    CGContextRef bitmap = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
    CGContextRotateCTM(bitmap, degrees * M_PI / 180);
    CGContextRotateCTM(bitmap, M_PI);
    CGContextScaleCTM(bitmap, -1.0, 1.0);
    CGContextDrawImage(bitmap, CGRectMake(-rotatedSize.width/2, -rotatedSize.height/2, rotatedSize.width, rotatedSize.height), self.CGImage);
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

MarkDown不会用,代码弄得很乱,但是经过几天的研究,可以很舒服的上代码了!