前言
在ios上邊使用地圖庫的同學(xué)肯定遇到過這樣的問題:吹出框只能設(shè)置title和subtitle和左右的view,不管是百度地圖還是高德地圖還是自帶的google地圖,只提供了這四個(gè)屬性,如果想添加更多的view,只能自定義。可是,類庫只能看到.h文件,.m都看不到,這讓新手比較蛋疼,龐大的地圖類庫一時(shí)半會(huì)摸不著頭腦,從頭再學(xué)還需要時(shí)間,本文就教大家快速制作一個(gè)屬于自己的 CalloutView!等你一步一步調(diào)通后,再回過頭來使用系統(tǒng)自帶的方法設(shè)置callout,就會(huì)領(lǐng)悟這個(gè)過程。
正文
Xcode版本:4.6.1
SDK版本:6.0?
百度地圖版本:1.2.2(關(guān)于地圖不必糾結(jié),無論是百度還是高德還是google都是基于系統(tǒng)的MapKit,都是一樣的)
demo模式:非ARC,使用storyboard。
demo資源:
http://download.csdn.net/detail/mad1989/5252037
Step1
創(chuàng)建demo,并添加百度地圖的靜態(tài)類庫,helloword能顯示mapview
關(guān)于這一步我專門寫了教程,這里就不再贅述,同樣,關(guān)于如何使用自帶的BMKPointAnnotation添加一個(gè)marker,我也不再說了,如果連這個(gè)你都不會(huì),那么先去官網(wǎng)看一下基本教程。
Step2
實(shí)現(xiàn)三個(gè)委托方法:
這個(gè)方法類似tableview添加cell,都是創(chuàng)建annotation
[cpp]?view plaincopy
#pragma?mark?? #pragma?mark?-?BMKMapview?delegate?? -(BMKAnnotationView?*)mapView:(BMKMapView?*)mapView?viewForAnnotation:(id<BMKAnnotation>)annotation;??
這個(gè)方法在點(diǎn)擊地圖marker時(shí)所觸發(fā)(并顯示callout)
[cpp]?view plaincopy
-(void)mapView:(BMKMapView?*)mapView?didSelectAnnotationView:(BMKAnnotationView?*)view;??
這個(gè)方法在點(diǎn)擊地圖任意位置,相當(dāng)于隱藏callout
[cpp]?view plaincopy
-(void)mapView:(BMKMapView?*)mapView?didDeselectAnnotationView:(BMKAnnotationView?*)view;??
原理:地圖上的marker是在viewForAnnoation里創(chuàng)建的,同時(shí)也會(huì)隱含的為我們創(chuàng)建一個(gè)CalloutView,就是自帶的吹出框,只是我們看不到源碼。其實(shí)這個(gè)吹出框(CalloutView)也是一個(gè)annotation,也會(huì)在viewForAnnotation里被創(chuàng)建,他的坐標(biāo)應(yīng)該和這個(gè)點(diǎn)的marker坐標(biāo)一樣,只要明白了這一點(diǎn),就行了,marker和吹出框是兩個(gè)不同的annotation,他們有同樣的coordinate。
Step3
自定義一個(gè)Annotation,為了簡單方便,我就直接繼承了mapview自帶的BMKPointAnnotation,這是一個(gè)經(jīng)典的圖釘marker。
在這里我添加了一個(gè)Dictionary屬性,目的是為了自定義的CalloutView吹出框顯示內(nèi)容賦值,一會(huì)就明白了。
Step4
添加自定義Annotation到mapview
[cpp]?view plaincopy
?? ?CLLocationCoordinate2D?center?=?{39.91669,116.39716};?? ?? CustomPointAnnotation?*pointAnnotation?=?[[CustomPointAnnotation?alloc]?init];?? pointAnnotation.title?=?@"我是中國人";?? pointAnnotation.subtitle?=?@"我愛自己的祖國";?? ?? pointAnnotation.coordinate?=?center;?? [mymapview?addAnnotation:pointAnnotation];?? [pointAnnotation?release];??
在viewForanntion里,由于我對marker沒太大要求,直接使用了
BMKPinAnnotationView(圖釘),簡單設(shè)置image屬性為自己需要的圖標(biāo),如下所示:
展示一個(gè)效果圖:
顯然CalloutView只能設(shè)置title和subtitle,無法滿足我們的要求,那么繼續(xù)下一步。
Step5
創(chuàng)建一個(gè)(自定義的CalloutView)的Annotation,相當(dāng)于顯示calloutView的annotation。
[注意] 繼承自NSObject<BMKAnnotation>
CalloutMapAnnotation.h
[cpp]?view plaincopy
#import?<Foundation/Foundation.h>?? #import?"BMapKit.h"?? ?? @interface?CalloutMapAnnotation?:?NSObject<BMKAnnotation>?? ?? ?? @property?(nonatomic)?CLLocationDegrees?latitude;?? @property?(nonatomic)?CLLocationDegrees?longitude;?? ?? ?? @property(retain,nonatomic)?NSDictionary?*locationInfo;?? ?? ?? ?? -?(id)initWithLatitude:(CLLocationDegrees)lat?andLongitude:(CLLocationDegrees)lon;?? ?? ?? ?? @end??
CalloutMapAnnotation.m
[cpp]?view plaincopy
#import?"CalloutMapAnnotation.h"?? ?? @implementation?CalloutMapAnnotation?? ?? ?? @synthesize?latitude;?? @synthesize?longitude;?? @synthesize?locationInfo;?? ?? -?(id)initWithLatitude:(CLLocationDegrees)lat?? ??????????andLongitude:(CLLocationDegrees)lon?{?? ????if?(self?=?[super?init])?{?? ????????self.latitude?=?lat;?? ????????self.longitude?=?lon;?? ????}?? ????return?self;?? }?? ?? ?? -(CLLocationCoordinate2D)coordinate{?? ?? ????CLLocationCoordinate2D?coordinate;?? ????coordinate.latitude?=?self.latitude;?? ????coordinate.longitude?=?self.longitude;?? ????return?coordinate;?? ?????? ?? }?? ?? ?? @end??
這里設(shè)置了經(jīng)緯度的屬性,和一個(gè)init初始化經(jīng)緯度的方法(經(jīng)緯度=marker的經(jīng)緯度),同樣添加了一個(gè)Dictionary的屬性,為了傳遞在CalloutView上內(nèi)容的賦值,繼續(xù)。
Step6
這一步我們創(chuàng)建自定義的View,想要什么布局就寫什么樣的布局,想要多少屬性就加多少屬性,這里我使用了code方式畫了一個(gè)contentView,里面的子view使用Xib方式創(chuàng)建。
[注意:繼承自BMKAnnotationView]
CallOutAnnotationView.h
[cpp]?view plaincopy
#import?"BMKAnnotationView.h"?? #import?"BusPointCell.h"?? ?? @interface?CallOutAnnotationView?:?BMKAnnotationView?? ?? ?? @property(nonatomic,retain)?UIView?*contentView;?? ?? ?? @property(nonatomic,retain)?BusPointCell?*busInfoView;?? ?? ?? @end??
BusPointCell是ContentView里的subview,這個(gè)view就是顯示各個(gè)組件,并賦不同的值
CallOutAnnotationView.m
[cpp]?view plaincopy
#import?"CallOutAnnotationView.h"?? #import?<QuartzCore/QuartzCore.h>?? ?? ?? #define??Arror_height?6?? ?? @implementation?CallOutAnnotationView?? @synthesize?contentView;?? @synthesize?busInfoView;?? ?? -?(id)initWithFrame:(CGRect)frame?? {?? ????self?=?[super?initWithFrame:frame];?? ????if?(self)?{?? ????}?? ????return?self;?? }?? ?? -(void)dealloc{?? ????[contentView?release];?? ????[busInfoView?release];?? ????[super?dealloc];?? }?? ?? -(id)initWithAnnotation:(id<BMKAnnotation>)annotation?reuseIdentifier:(NSString?*)reuseIdentifier{?? ?? ?????? ????self?=?[super?initWithAnnotation:annotation?reuseIdentifier:reuseIdentifier];?? ????if?(self)?{?? ????????self.backgroundColor?=?[UIColor?clearColor];?? ????????self.canShowCallout?=?NO;?? ????????self.centerOffset?=?CGPointMake(0,?-55);?? ????????self.frame?=?CGRectMake(0,?0,?240,?80);?? ?? ????????UIView?*_contentView?=?[[UIView?alloc]?initWithFrame:CGRectMake(5,?5,?self.frame.size.width-15,?self.frame.size.height-15)];?? ????????_contentView.backgroundColor???=?[UIColor?clearColor];?? ????????[self?addSubview:_contentView];?? ????????self.contentView?=?_contentView;?? ????????[_contentView?release];?? ????}?? ????return?self;?? ?? }?? ?? -(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?colorWithRed:255.0/255.0?green:255.0/255.0?blue:255.0/255.0?alpha:1.0].CGColor);?? ?????? ????[self?getDrawPath:context];?? ????CGContextFillPath(context);?? ?????? }?? -?(void)getDrawPath:(CGContextRef)context?? {?? ????CGRect?rrect?=?self.bounds;?? ????CGFloat?radius?=?6.0;?? ?????? ????CGFloat?minx?=?CGRectGetMinX(rrect),?? ????midx?=?CGRectGetMidX(rrect),?? ????maxx?=?CGRectGetMaxX(rrect);?? ????CGFloat?miny?=?CGRectGetMinY(rrect),?? ?????? ????maxy?=?CGRectGetMaxY(rrect)-Arror_height;?? ????CGContextMoveToPoint(context,?midx+Arror_height,?maxy);?? ????CGContextAddLineToPoint(context,midx,?maxy+Arror_height);?? ????CGContextAddLineToPoint(context,midx-Arror_height,?maxy);?? ?????? ????CGContextAddArcToPoint(context,?minx,?maxy,?minx,?miny,?radius);?? ????CGContextAddArcToPoint(context,?minx,?minx,?maxx,?miny,?radius);?? ????CGContextAddArcToPoint(context,?maxx,?miny,?maxx,?maxx,?radius);?? ????CGContextAddArcToPoint(context,?maxx,?maxy,?midx,?maxy,?radius);?? ????CGContextClosePath(context);?? }?? ?? ?? ?? @end??
BusPointCell.h
想要多少label,就可以有多少label
[cpp]?view plaincopy
#import?<UIKit/UIKit.h>?? ?? @interface?BusPointCell?:?UIView?? @property?(retain,?nonatomic)?IBOutlet?UILabel?*aliasLabel;?? @property?(retain,?nonatomic)?IBOutlet?UILabel?*speedLabel;?? @property?(retain,?nonatomic)?IBOutlet?UILabel?*degreeLabel;?? @property?(retain,?nonatomic)?IBOutlet?UILabel?*nameLabel;?? ?? @end??
BusPointCell.m
[cpp]?view plaincopy
#import?"BusPointCell.h"?? ?? @implementation?BusPointCell?? ?? -?(id)initWithFrame:(CGRect)frame?? {?? ????self?=?[super?initWithFrame:frame];?? ????if?(self)?{?? ?? ????}?? ????return?self;?? }?? -?(void)dealloc?{?? ????[_aliasLabel?release];?? ????[_speedLabel?release];?? ????[_degreeLabel?release];?? ????[_nameLabel?release];?? ????[super?dealloc];?? }?? @end??
BusPointCell.xib
Step7
自定義的CalloutView都準(zhǔn)備妥當(dāng),現(xiàn)在就是要實(shí)現(xiàn)他們的部分了,簡單說一下原理,在didSelectAnnotationView函數(shù)里創(chuàng)建并添加calloutview的annotation(CalloutMapAnnotation),然后在viewForAnnotation函數(shù)內(nèi)實(shí)例化要顯示的calloutview(CallOutAnnotationView)
首先聲明一個(gè)局部變量CalloutMapAnnotation?*_calloutMapAnnotation;
在didSelectAnnotationView函數(shù)內(nèi)添加如下代碼:
[cpp]?view plaincopy
?? CustomPointAnnotation?*annn?=?(CustomPointAnnotation*)view.annotation;?? ?? ?? if?([view.annotation?isKindOfClass:[CustomPointAnnotation?class]])?{?? ?????? ?????? ????if?(_calloutMapAnnotation.coordinate.latitude?==?view.annotation.coordinate.latitude&&?? ????????_calloutMapAnnotation.coordinate.longitude?==?view.annotation.coordinate.longitude)?{?? ????????return;?? ????}?? ?????? ????if?(_calloutMapAnnotation)?{?? ????????[mapView?removeAnnotation:_calloutMapAnnotation];?? ????????_calloutMapAnnotation=nil;?? ?????????? ????}?? ?????? ????_calloutMapAnnotation?=?[[[CalloutMapAnnotation?alloc]?initWithLatitude:view.annotation.coordinate.latitude?andLongitude:view.annotation.coordinate.longitude]?autorelease];?? ?????? ?????? ????_calloutMapAnnotation.locationInfo?=?annn.pointCalloutInfo;?? ?????? ????[mapView?addAnnotation:_calloutMapAnnotation];?? ?? ?????? ?????? ????[mapView?setCenterCoordinate:view.annotation.coordinate?animated:YES];?? ?????? }??
其次,要在viewForAnnotation里創(chuàng)建我們的calloutview(CallOutAnnotationView),添加如下代碼:
[cpp]?view plaincopy
else?if?([annotation?isKindOfClass:[CalloutMapAnnotation?class]]){?? ?????? ?????? ????CalloutMapAnnotation?*ann?=?(CalloutMapAnnotation*)annotation;?? ?????? ?????? ????CallOutAnnotationView?*calloutannotationview?=?(CallOutAnnotationView?*)[mapView?dequeueReusableAnnotationViewWithIdentifier:@"calloutview"];?? ?????? ?????? ????if?(!calloutannotationview)?{?? ????????calloutannotationview?=?[[[CallOutAnnotationView?alloc]?initWithAnnotation:annotation?reuseIdentifier:@"calloutview"]?autorelease];?? ?? ????????BusPointCell?*cell?=?[[[NSBundle?mainBundle]?loadNibNamed:@"BusPointCell"?owner:self?options:nil]?objectAtIndex:0];?? ?????????? ????????[calloutannotationview.contentView?addSubview:cell];?? ????????calloutannotationview.busInfoView?=?cell;?? ????}?? ?????? ?????? ????calloutannotationview.busInfoView.aliasLabel.text?=?[ann.locationInfo?objectForKey:@"alias"];?? ????calloutannotationview.busInfoView.speedLabel.text?=?[ann.locationInfo?objectForKey:@"speed"];?? ????calloutannotationview.busInfoView.degreeLabel.text?=[ann.locationInfo?objectForKey:@"degree"];?? ????calloutannotationview.busInfoView.nameLabel.text?=??[ann.locationInfo?objectForKey:@"name"];?? ?????? ????return?calloutannotationview;?? ?????? }??
[注意]在添加marker的判斷里一定要設(shè)置marker
annotation.canShowCallout?=NO; 否則點(diǎn)擊marker會(huì)默認(rèn)顯示系統(tǒng)的吹出框
Step8
calloutview的annotation也創(chuàng)建和添加了,接下來我們就設(shè)置一下marker對應(yīng)吹出框的數(shù)據(jù):
然后運(yùn)行一下:
哈哈!搞定了吧,具體布局可以自己通過code方式,或xib方式設(shè)計(jì),目前點(diǎn)擊marker能顯示了,可是點(diǎn)擊其它區(qū)域還是無法顯示,所以我們在didDeselectAnnotationView方法里還需要判斷一下,remove掉。
[cpp]?view plaincopy
-(void)mapView:(BMKMapView?*)mapView?didDeselectAnnotationView:(BMKAnnotationView?*)view{?? ?????? ????if?(_calloutMapAnnotation&&![view?isKindOfClass:[CallOutAnnotationView?class]])?{?? ?? ????????if?(_calloutMapAnnotation.coordinate.latitude?==?view.annotation.coordinate.latitude&&?? ????????????_calloutMapAnnotation.coordinate.longitude?==?view.annotation.coordinate.longitude)?{?? ????????????[mapView?removeAnnotation:_calloutMapAnnotation];?? ????????????_calloutMapAnnotation?=?nil;?? ????????}?? ?????????? ?????????? ????}?? ?? }??
最后
之所以在顯示marker的annotation[本文為CustomPointAnnotation]和顯示calloutview的annotation[本文為CalloutMapAnnotation]里各添加一個(gè)Dictionary,就是要在點(diǎn)擊時(shí)通過marker傳遞數(shù)據(jù),添加時(shí)通過calloutview的annotation實(shí)例來設(shè)置每一個(gè)屬性的數(shù)據(jù),已達(dá)到不同的maker,顯示不同的數(shù)據(jù)。
可能我的過程不是太清晰,自己仔細(xì)研究一下這三個(gè)函數(shù)和mapview自帶的callout調(diào)用過程,便會(huì)明白
本文轉(zhuǎn)自:http://blog.csdn.net/mad1989/article/details/8794762
總結(jié)
以上是生活随笔為你收集整理的ios 一步一步学会自定义地图吹出框(CalloutView)--(百度地图,高德地图,google地图)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。