自定義高德地圖大頭針氣泡

當(dāng)高德地圖自帶的大頭針氣泡不能滿足項(xiàng)目需求時(shí),需要開(kāi)發(fā)者自定義地圖大頭針氣泡。比如氣泡上顯示個(gè)圖片,標(biāo)題或者其他之類的。

一、高德地圖“氣泡”的本質(zhì)是什么?

  • 大頭針視圖
    大頭針顯示在地圖上是一個(gè)圖片,圖片是放在imageView上的。高德地圖上先是有一個(gè)view(MAAnnotationView),然后這個(gè)view上有一個(gè)imageView,imageView上放一張圖片,就是我們?cè)诘貓D上所看到的大頭針了。

  • 大頭針氣泡視圖
    大頭針氣泡(CalloutView)一般是顯示在大頭針(MAPointAnnotation)頂部的一個(gè)視圖,歸根結(jié)底是一個(gè)view。它是添加在大頭針視圖(MAAnnotationView)上的一個(gè)視圖。

  • MAAnnotationView、CalloutView、MAPointAnnotation
    MAAnnotationView是高德地圖提供的大頭針視圖view,繼承自UIView,高德對(duì)其進(jìn)行了封裝,有很多屬性可以看看高德文檔。其中每個(gè)MAAnnotationView都關(guān)聯(lián)一個(gè)annotation,annotation是MAPointAnnotation的對(duì)象。
    CalloutView是大頭針氣泡視圖,高德自己的沒(méi)有完全暴露在外部。
    MAPointAnnotation,每個(gè)大頭針視圖都關(guān)聯(lián)一個(gè)annotation,annotation有title、subtitle、還有經(jīng)緯度信息。就把它看做是一個(gè)給MAAnnotationView提供數(shù)據(jù)支撐的model吧。

二、自定義高德地圖大頭針氣泡

要實(shí)現(xiàn)自定義的大頭針氣泡,不能用高德自己的大頭針視圖MAAnnotationView,因?yàn)槲覀円oMAAnnotationView增加一個(gè)calloutView屬性來(lái)實(shí)現(xiàn)自定義的大頭針氣泡功能。

需要三步:

1、這里我們只需要繼承自MAAnnotationView來(lái)創(chuàng)建一個(gè)自己的大頭針視圖:DDCustomAnnotationView。

2、然后在自定義一個(gè)繼承自UIView的視圖作為氣泡:DDCustomCalloutView。

3、在地圖的代理方法中設(shè)置自定義的大頭針氣泡

第一步:自定義一個(gè)繼承UIView的大頭針氣泡視圖DDCustomCalloutView

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

@interface DDCustomCalloutView : UIView

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

@property (nonatomic, strong) UILabel *leftNumLabel;//氣泡上的數(shù)字標(biāo)記

@end

  • 這里只自定義了一個(gè)label,大家可根據(jù)自己的實(shí)際情況自己定義更加豐富的氣泡視圖都是可以的。
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();
    // 移動(dòng)到初始點(diǎn)
    CGContextMoveToPoint(context, radius, 0);
    // 繪制第1條線和第1個(gè)1/4圓弧
    CGContextAddLineToPoint(context, width - radius, 0);
    CGContextAddArc(context, width - radius, radius, radius, -0.5 * M_PI, 0.0, 0);
    // 繪制第2條線和第2個(gè)1/4圓弧
    CGContextAddLineToPoint(context, width, height - radius);
    CGContextAddArc(context, width - radius, height - radius, radius, 0.0, 0.5 * M_PI, 0);
    // 繪制第3條線和第3個(gè)1/4圓弧
    CGContextAddLineToPoint(context, radius, height);
    CGContextAddArc(context, radius, height - radius, radius, 0.5 * M_PI, M_PI, 0);
    // 繪制第4條線和第4個(gè)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
  • 感覺(jué)寫(xiě)的有點(diǎn)繁瑣復(fù)雜,我在想如果不用drawRect方法,直接創(chuàng)建視圖也是可以實(shí)現(xiàn)的。

第二步:繼承MAAnnotationView自定義一個(gè)大頭針視圖DDCustomAnnotationView

DDCustomAnnotationView.h
#import "DDCustomAnnotationView.h"

#define kCalloutWidth       120.0
#define kCalloutHeight      45.0

@implementation DDCustomAnnotationView

//復(fù)寫(xiě)父類init方法
- (id)initWithAnnotation:(id<MAAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier])
    {
        //創(chuàng)建大頭針視圖
        [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];
    /*坐標(biāo)不合適再此設(shè)置即可*/
    //Code ...
    self.calloutView.frame = CGRectMake(-(kCalloutWidth-self.frame.size.width)*0.5, -kCalloutHeight, kCalloutWidth, kCalloutHeight);
}

@end

第三步:加載地圖,添加大頭針,顯示氣泡

以下代碼寫(xiě)的比較low,建議大家在使用高德地圖的時(shí)候根據(jù)自己項(xiàng)目實(shí)際情況封裝后再使用。

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 = @"國(guó)貿(mào)";
    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];
    //設(shè)置地圖范圍
    [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
  • 這里我們把地圖放在了自己寫(xiě)了一個(gè)map類中,這個(gè)類是繼承自UIView的,上面是個(gè)地圖。里面實(shí)現(xiàn)了mapView的代理方法,添加了大頭針視圖和大頭針氣泡等設(shè)置。
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 根據(jù)anntation生成對(duì)應(yīng)的View。
 * @param mapView 地圖View
 * @param annotation 指定的標(biāo)注
 * @return 生成的標(biāo)注View
 */
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation {
    /*這里根據(jù)不同類型的大頭針,生成不同的大頭針視圖
     為了方便起見(jiàn)我們繼承MAPointAnnotation創(chuàng)建了自己的DDAnnotation,用來(lái)擴(kuò)展更多屬性,給大頭針視圖提供更多數(shù)據(jù)等
     */
    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;//設(shè)置此屬性為NO,防止點(diǎn)擊的時(shí)候高德自帶的氣泡彈出
        }
        
        //給氣泡賦值
        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;//設(shè)置大頭針圖片
        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
  • 這個(gè)可以根據(jù)自己項(xiàng)目的情況自己進(jìn)行改寫(xiě)。

三、總結(jié)

  • 自定義高德地圖氣泡大體思路就是這樣,高德開(kāi)放平臺(tái)上的demo和這里相差無(wú)幾。其中細(xì)節(jié)大家可做優(yōu)化或者根據(jù)自己的項(xiàng)目需要做不同的封裝。

  • 除此之外我還試過(guò)直接找到MAAnnotationView的位置,直接在地圖上添加一個(gè)view的做法,也是可以實(shí)現(xiàn)的,看項(xiàng)目需要了。

  • 再此基礎(chǔ)上做大頭針數(shù)據(jù)的時(shí)時(shí)刷新等功能也是可以的,更多功能可以盡情擴(kuò)展。

四、demo

實(shí)現(xiàn)效果比較粗糙,隨便看看即可,更多功能還需自己擴(kuò)展。運(yùn)行demo的時(shí)候別忘了自己在AppDelegate里面設(shè)置高德AppKey。

myDemo.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容