自定义高德地图大头针气泡

当高德地图自带的大头针气泡不能满足项目需求时,需要开发者自定义地图大头针气泡。比如气泡上显示个图片,标题或者其他之类的。

一、高德地图“气泡”的本质是什么?

  • 大头针视图
    大头针显示在地图上是一个图片,图片是放在imageView上的。高德地图上先是有一个view(MAAnnotationView),然后这个view上有一个imageView,imageView上放一张图片,就是我们在地图上所看到的大头针了。

  • 大头针气泡视图
    大头针气泡(CalloutView)一般是显示在大头针(MAPointAnnotation)顶部的一个视图,归根结底是一个view。它是添加在大头针视图(MAAnnotationView)上的一个视图。

  • MAAnnotationView、CalloutView、MAPointAnnotation
    MAAnnotationView是高德地图提供的大头针视图view,继承自UIView,高德对其进行了封装,有很多属性可以看看高德文档。其中每个MAAnnotationView都关联一个annotation,annotation是MAPointAnnotation的对象。
    CalloutView是大头针气泡视图,高德自己的没有完全暴露在外部。
    MAPointAnnotation,每个大头针视图都关联一个annotation,annotation有title、subtitle、还有经纬度信息。就把它看做是一个给MAAnnotationView提供数据支撑的model吧。

二、自定义高德地图大头针气泡

要实现自定义的大头针气泡,不能用高德自己的大头针视图MAAnnotationView,因为我们要给MAAnnotationView增加一个calloutView属性来实现自定义的大头针气泡功能。

需要三步:

1、这里我们只需要继承自MAAnnotationView来创建一个自己的大头针视图:DDCustomAnnotationView。

2、然后在自定义一个继承自UIView的视图作为气泡:DDCustomCalloutView。

3、在地图的代理方法中设置自定义的大头针气泡

第一步:自定义一个继承UIView的大头针气泡视图DDCustomCalloutView

DDCustomCalloutView.h
#import <UIKit/UIKit.h>

@interface DDCustomCalloutView : UIView

@property (nonatomic, strong) UILabel *textLabel;//气泡上的label

@property (nonatomic, strong) UILabel *leftNumLabel;//气泡上的数字标记

@end

  • 这里只自定义了一个label,大家可根据自己的实际情况自己定义更加丰富的气泡视图都是可以的。
DDCustomCalloutView.m
#import "DDCustomCalloutView.h"

#define kArrorHeight 10

@implementation DDCustomCalloutView

- (void)drawRect:(CGRect)rect
{
    CGFloat width = rect.size.width;
    CGFloat height = rect.size.height-kArrorHeight;
    //圆角半径
    CGFloat radius = (self.frame.size.height-kArrorHeight)*0.5;
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 移动到初始点
    CGContextMoveToPoint(context, radius, 0);
    // 绘制第1条线和第1个1/4圆弧
    CGContextAddLineToPoint(context, width - radius, 0);
    CGContextAddArc(context, width - radius, radius, radius, -0.5 * M_PI, 0.0, 0);
    // 绘制第2条线和第2个1/4圆弧
    CGContextAddLineToPoint(context, width, height - radius);
    CGContextAddArc(context, width - radius, height - radius, radius, 0.0, 0.5 * M_PI, 0);
    // 绘制第3条线和第3个1/4圆弧
    CGContextAddLineToPoint(context, radius, height);
    CGContextAddArc(context, radius, height - radius, radius, 0.5 * M_PI, M_PI, 0);
    // 绘制第4条线和第4个1/4圆弧
    CGContextAddLineToPoint(context, 0, radius);
    CGContextAddArc(context, radius, radius, radius, M_PI, 1.5 * M_PI, 0);
    // 闭合路径
    CGContextClosePath(context);
    // 填充半透明黑色
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextDrawPath(context, kCGPathFill);
    
    self.layer.shadowColor = [[UIColor grayColor] CGColor];
    self.layer.shadowOpacity = 1.0;
    self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        self.backgroundColor = [UIColor clearColor];
        
        [self setUpUI];
    }
    return self;
}

#pragma mark UI
- (void)setUpUI {
    self.textLabel = [[UILabel alloc]initWithFrame:CGRectMake(8, 0, self.frame.size.width-16, self.frame.size.height-kArrorHeight)];
    self.textLabel.backgroundColor = [UIColor clearColor];
    self.textLabel.textAlignment = NSTextAlignmentCenter;
    self.textLabel.font = [UIFont systemFontOfSize:15.0];
    self.textLabel.textColor = [UIColor lightGrayColor];
    [self addSubview:self.textLabel];

    self.leftNumLabel = [[UILabel alloc]initWithFrame:CGRectMake(8, 0, 20, self.frame.size.height-kArrorHeight)];
    self.leftNumLabel.backgroundColor = [UIColor clearColor];
    self.leftNumLabel.textAlignment = NSTextAlignmentCenter;
    self.leftNumLabel.font = [UIFont systemFontOfSize:15.0];
    self.leftNumLabel.textColor = [UIColor redColor];
    [self addSubview:self.leftNumLabel];
}

@end
  • 感觉写的有点繁琐复杂,我在想如果不用drawRect方法,直接创建视图也是可以实现的。

第二步:继承MAAnnotationView自定义一个大头针视图DDCustomAnnotationView

DDCustomAnnotationView.h
#import "DDCustomAnnotationView.h"

#define kCalloutWidth       120.0
#define kCalloutHeight      45.0

@implementation DDCustomAnnotationView

//复写父类init方法
- (id)initWithAnnotation:(id<MAAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier])
    {
        //创建大头针视图
        [self setUpClloutView];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    if (self.selected == selected)
    {
        return;
    }
    if (selected)
    {
        [self setUpClloutView];
    }
    else
    {
    }
    [super setSelected:selected animated:animated];
}

- (void)setUpClloutView {
    if (self.calloutView == nil)
    {
        self.calloutView = [[DDCustomCalloutView alloc] initWithFrame:CGRectMake(0, 0, kCalloutWidth, kCalloutHeight)];
        [self addSubview:self.calloutView];
    }
}

- (void)layoutSubviews {
    [super layoutSubviews];
    /*坐标不合适再此设置即可*/
    //Code ...
    self.calloutView.frame = CGRectMake(-(kCalloutWidth-self.frame.size.width)*0.5, -kCalloutHeight, kCalloutWidth, kCalloutHeight);
}

@end

第三步:加载地图,添加大头针,显示气泡

以下代码写的比较low,建议大家在使用高德地图的时候根据自己项目实际情况封装后再使用。

ViewController.m中
#import "ViewController.h"
#import "Map.h"
#import "DDPointAnnotation.h"

@interface ViewController ()

@property (nonatomic, strong) Map *map;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //添加地图
    [self.view addSubview:self.map];
    //地图上添加大头针
    DDPointAnnotation *start = [[DDPointAnnotation alloc] init];
    start.title = @"国贸";
    start.number = @"1";
    start.image = [UIImage imageNamed:@"start"];
    start.coordinate = CLLocationCoordinate2DMake(39.90841537, 116.45969689);
    [self.map.mapView addAnnotation:start];
    
    DDPointAnnotation *end = [[DDPointAnnotation alloc] init];
    end.title = @"故宫";
    end.number = @"2";
    end.image = [UIImage imageNamed:@"end"];
    end.coordinate = CLLocationCoordinate2DMake(39.92000603, 116.39465332);
    [self.map.mapView addAnnotation:end];
    //设置地图范围
    [self.map.mapView showAnnotations:@[start,end] animated:NO];
}

- (Map *)map {
    if (!_map) {
        _map = [[Map alloc] initWithFrame:self.view.bounds];
    }
    return _map;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end
  • 这里我们把地图放在了自己写了一个map类中,这个类是继承自UIView的,上面是个地图。里面实现了mapView的代理方法,添加了大头针视图和大头针气泡等设置。
Map.h
#import <UIKit/UIKit.h>
#import <MAMapKit/MAMapKit.h>

@interface Map : UIView

@property (nonatomic, strong) MAMapView *mapView;//地图

@end
Map.m
#import "Map.h"
#import "DDPointAnnotation.h"
#import "DDCustomAnnotationView.h"

@interface Map ()<MAMapViewDelegate>

@end

@implementation Map

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        self.backgroundColor = [UIColor clearColor];
        
        [self addSubview:self.mapView];
    }
    return self;
}

#pragma mark MAMapViewDelegate
/**
 * @brief 根据anntation生成对应的View。
 * @param mapView 地图View
 * @param annotation 指定的标注
 * @return 生成的标注View
 */
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation {
    /*这里根据不同类型的大头针,生成不同的大头针视图
     为了方便起见我们继承MAPointAnnotation创建了自己的DDAnnotation,用来扩展更多属性,给大头针视图提供更多数据等
     */
    if ([annotation isKindOfClass:[DDPointAnnotation class]])
    {
        static NSString *reusedID = @"DDPointAnnotation_reusedID";
        DDCustomAnnotationView *annotationView = (DDCustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reusedID];
        
        if (!annotationView) {
            annotationView = [[DDCustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reusedID];
            annotationView.canShowCallout = NO;//设置此属性为NO,防止点击的时候高德自带的气泡弹出
        }
        
        //给气泡赋值
        DDPointAnnotation *ddAnnotation = (DDPointAnnotation *)annotation;
        NSLog(@"********* %@ %@",ddAnnotation.title,ddAnnotation.number);
        annotationView.calloutView.textLabel.text = ddAnnotation.title;
        annotationView.calloutView.leftNumLabel.text = ddAnnotation.number;
        
        annotationView.image = ddAnnotation.image;//设置大头针图片
        annotationView.centerOffset = CGPointMake(0, -0.5*ddAnnotation.image.size.height);

        return annotationView;
    }
    
    return nil;
}

#pragma mark lazy load
- (MAMapView *)mapView {
    if (!_mapView) {
        _mapView = [[MAMapView alloc] initWithFrame:self.bounds];
        _mapView.showsScale = NO;
        _mapView.showsCompass = NO;
        _mapView.rotateEnabled = NO;
        _mapView.delegate = self;
    }
    return _mapView;
}

@end
  • 这个可以根据自己项目的情况自己进行改写。

三、总结

  • 自定义高德地图气泡大体思路就是这样,高德开放平台上的demo和这里相差无几。其中细节大家可做优化或者根据自己的项目需要做不同的封装。

  • 除此之外我还试过直接找到MAAnnotationView的位置,直接在地图上添加一个view的做法,也是可以实现的,看项目需要了。

  • 再此基础上做大头针数据的时时刷新等功能也是可以的,更多功能可以尽情扩展。

四、demo

实现效果比较粗糙,随便看看即可,更多功能还需自己扩展。运行demo的时候别忘了自己在AppDelegate里面设置高德AppKey。

myDemo.png

地址:https://github.com/Mexiang/New_Map_CallouView

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容