ios 一步一步學會自定義地圖吹出框(CalloutView)-->(百度地圖,高德地圖,google地圖)

 ios 一步一步學會自定義地圖吹出框(CalloutView)-->(百度地圖,高德地圖,google地圖)

高德地圖google地圖百度地圖吹出框自定義




前言


在ios上邊使用地圖庫的同學肯定遇到過這樣的問題:吹出框只能設置title和subtitle和左右的view,不管是百度地圖還是高德地圖還是自帶的google地圖,只提供了這四個屬性,如果想添加更多的view,只能自定義。可是,類庫只能看到.h文件,.m都看不到,這讓新手比較蛋疼,龐大的地圖類庫一時半會摸不着頭腦,從頭再學還需要時間,本文就教大家快速製作一個屬於自己的 CalloutView!等你一步一步調通後,再回過頭來使用系統自帶的方法設置callout,就會領悟這個過程。




正文


Xcode版本:4.6.1


SDK版本:6.0 


百度地圖版本:1.2.2(關於地圖不必糾結,無論是百度還是高德還是google都是基於系統的MapKit,都是一樣的)


demo模式:非ARC,使用storyboard。


demo資源:

http://download.csdn.net/detail/mad1989/5252037


Step1

創建demo,並添加百度地圖的靜態類庫,helloword能顯示mapview


關於這一步我專門寫了教程,這裏就不再贅述,同樣,關於如何使用自帶的BMKPointAnnotation添加一個marker,我也不再說了,如果連這個你都不會,那麼先去官網看一下基本教程。


Step2

實現三個委託方法:

這個方法類似tableview添加cell,都是創建annotation

  1. #pragma mark  
  2. #pragma mark - BMKMapview delegate  
  3. -(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation;  


這個方法在點擊地圖marker時所觸發(並顯示callout)
  1. -(void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view;  


這個方法在點擊地圖任意位置,相當於隱藏callout

  1. -(void)mapView:(BMKMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view;  



原理:地圖上的marker是在viewForAnnoation裏創建的,同時也會隱含的爲我們創建一個CalloutView,就是自帶的吹出框,只是我們看不到源碼。其實這個吹出框(CalloutView)也是一個annotation,也會在viewForAnnotation裏被創建,他的座標應該和這個點的marker座標一樣,只要明白了這一點,就行了,marker和吹出框是兩個不同的annotation,他們有同樣的coordinate


Step3

自定義一個Annotation,爲了簡單方便,我就直接繼承了mapview自帶的BMKPointAnnotation,這是一個經典的圖釘marker。


在這裏我添加了一個Dictionary屬性,目的是爲了自定義的CalloutView吹出框顯示內容賦值,一會就明白了。



Step4

添加自定義Annotation到mapview

  1. //添加自定義Annotation  
  2.  CLLocationCoordinate2D center = {39.91669,116.39716};  
  3.   
  4. CustomPointAnnotation *pointAnnotation = [[CustomPointAnnotation alloc] init];  
  5. pointAnnotation.title = @"我是中國人";//因爲繼承了BMKPointAnnotation,所以這些title,subtitle都可以設置  
  6. pointAnnotation.subtitle = @"我愛自己的祖國";  
  7.   
  8. pointAnnotation.coordinate = center;  
  9. [mymapview addAnnotation:pointAnnotation];  
  10. [pointAnnotation release];  

在viewForanntion裏,由於我對marker沒太大要求,直接使用了BMKPinAnnotationView(圖釘),簡單設置image屬性爲自己需要的圖標,如下所示:


展示一個效果圖:



顯然CalloutView只能設置title和subtitle,無法滿足我們的要求,那麼繼續下一步。


Step5

創建一個(自定義的CalloutView)的Annotation,相當於顯示calloutView的annotation。

[注意] 繼承自NSObject<BMKAnnotation>

CalloutMapAnnotation.h

  1. #import <Foundation/Foundation.h>  
  2. #import "BMapKit.h"  
  3.   
  4. @interface CalloutMapAnnotation : NSObject<BMKAnnotation>  
  5.   
  6.   
  7. @property (nonatomic) CLLocationDegrees latitude;  
  8. @property (nonatomic) CLLocationDegrees longitude;  
  9.   
  10.   
  11. @property(retain,nonatomic) NSDictionary *locationInfo;//callout吹出框要顯示的各信息  
  12.   
  13.   
  14.   
  15. - (id)initWithLatitude:(CLLocationDegrees)lat andLongitude:(CLLocationDegrees)lon;  
  16.   
  17.   
  18.   
  19. @end  

CalloutMapAnnotation.m

  1. #import "CalloutMapAnnotation.h"  
  2.   
  3. @implementation CalloutMapAnnotation  
  4.   
  5.   
  6. @synthesize latitude;  
  7. @synthesize longitude;  
  8. @synthesize locationInfo;  
  9.   
  10. - (id)initWithLatitude:(CLLocationDegrees)lat  
  11.           andLongitude:(CLLocationDegrees)lon {  
  12.     if (self = [super init]) {  
  13.         self.latitude = lat;  
  14.         self.longitude = lon;  
  15.     }  
  16.     return self;  
  17. }  
  18.   
  19.   
  20. -(CLLocationCoordinate2D)coordinate{  
  21.   
  22.     CLLocationCoordinate2D coordinate;  
  23.     coordinate.latitude = self.latitude;  
  24.     coordinate.longitude = self.longitude;  
  25.     return coordinate;  
  26.       
  27.   
  28. }  
  29.   
  30.   
  31. @end  

這裏設置了經緯度的屬性,和一個init初始化經緯度的方法(經緯度=marker的經緯度),同樣添加了一個Dictionary的屬性,爲了傳遞在CalloutView上內容的賦值,繼續。


Step6

這一步我們創建自定義的View,想要什麼佈局就寫什麼樣的佈局,想要多少屬性就加多少屬性,這裏我使用了code方式畫了一個contentView,裏面的子view使用Xib方式創建。

[注意:繼承自BMKAnnotationView]


CallOutAnnotationView.h

  1. #import "BMKAnnotationView.h"  
  2. #import "BusPointCell.h"  
  3.   
  4. @interface CallOutAnnotationView : BMKAnnotationView  
  5.   
  6.   
  7. @property(nonatomic,retain) UIView *contentView;  
  8.   
  9. //添加一個UIView  
  10. @property(nonatomic,retain) BusPointCell *busInfoView;//在創建calloutView Annotation時,把contentView add的 subview賦值給businfoView  
  11.   
  12.   
  13. @end  

BusPointCell是ContentView裏的subview,這個view就是顯示各個組件,並賦不同的值

CallOutAnnotationView.m

  1. #import "CallOutAnnotationView.h"  
  2. #import <QuartzCore/QuartzCore.h>  
  3.   
  4.   
  5. #define  Arror_height 6  
  6.   
  7. @implementation CallOutAnnotationView  
  8. @synthesize contentView;  
  9. @synthesize busInfoView;  
  10.   
  11. - (id)initWithFrame:(CGRect)frame  
  12. {  
  13.     self = [super initWithFrame:frame];  
  14.     if (self) {  
  15.     }  
  16.     return self;  
  17. }  
  18.   
  19. -(void)dealloc{  
  20.     [contentView release];  
  21.     [busInfoView release];  
  22.     [super dealloc];  
  23. }  
  24.   
  25. -(id)initWithAnnotation:(id<BMKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{  
  26.   
  27.       
  28.     self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];  
  29.     if (self) {  
  30.         self.backgroundColor = [UIColor clearColor];  
  31.         self.canShowCallout = NO;  
  32.         self.centerOffset = CGPointMake(0, -55);  
  33.         self.frame = CGRectMake(0, 0, 240, 80);  
  34.   
  35.         UIView *_contentView = [[UIView alloc] initWithFrame:CGRectMake(5, 5, self.frame.size.width-15, self.frame.size.height-15)];  
  36.         _contentView.backgroundColor   = [UIColor clearColor];  
  37.         [self addSubview:_contentView];  
  38.         self.contentView = _contentView;  
  39.         [_contentView release];  
  40.     }  
  41.     return self;  
  42.   
  43. }  
  44.   
  45. -(void)drawRect:(CGRect)rect{  
  46.   
  47.     [self drawInContext:UIGraphicsGetCurrentContext()];  
  48.       
  49.     self.layer.shadowColor = [[UIColor blackColor] CGColor];  
  50.     self.layer.shadowOpacity = 1.0;  
  51.     self.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);  
  52.       
  53.   
  54. }  
  55.   
  56. -(void)drawInContext:(CGContextRef)context  
  57. {  
  58.       
  59.     CGContextSetLineWidth(context, 2.0);  
  60.     CGContextSetFillColorWithColor(context, [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0].CGColor);  
  61.       
  62.     [self getDrawPath:context];  
  63.     CGContextFillPath(context);  
  64.       
  65. }  
  66. - (void)getDrawPath:(CGContextRef)context  
  67. {  
  68.     CGRect rrect = self.bounds;  
  69.     CGFloat radius = 6.0;  
  70.       
  71.     CGFloat minx = CGRectGetMinX(rrect),  
  72.     midx = CGRectGetMidX(rrect),  
  73.     maxx = CGRectGetMaxX(rrect);  
  74.     CGFloat miny = CGRectGetMinY(rrect),  
  75.     // midy = CGRectGetMidY(rrect),  
  76.     maxy = CGRectGetMaxY(rrect)-Arror_height;  
  77.     CGContextMoveToPoint(context, midx+Arror_height, maxy);  
  78.     CGContextAddLineToPoint(context,midx, maxy+Arror_height);  
  79.     CGContextAddLineToPoint(context,midx-Arror_height, maxy);  
  80.       
  81.     CGContextAddArcToPoint(context, minx, maxy, minx, miny, radius);  
  82.     CGContextAddArcToPoint(context, minx, minx, maxx, miny, radius);  
  83.     CGContextAddArcToPoint(context, maxx, miny, maxx, maxx, radius);  
  84.     CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);  
  85.     CGContextClosePath(context);  
  86. }  
  87.   
  88.   
  89.   
  90. @end  

BusPointCell.h

想要多少label,就可以有多少label

  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface BusPointCell : UIView  
  4. @property (retain, nonatomic) IBOutlet UILabel *aliasLabel;  
  5. @property (retain, nonatomic) IBOutlet UILabel *speedLabel;  
  6. @property (retain, nonatomic) IBOutlet UILabel *degreeLabel;  
  7. @property (retain, nonatomic) IBOutlet UILabel *nameLabel;  
  8.   
  9. @end  

BusPointCell.m

  1. #import "BusPointCell.h"  
  2.   
  3. @implementation BusPointCell  
  4.   
  5. - (id)initWithFrame:(CGRect)frame  
  6. {  
  7.     self = [super initWithFrame:frame];  
  8.     if (self) {  
  9.   
  10.     }  
  11.     return self;  
  12. }  
  13. - (void)dealloc {  
  14.     [_aliasLabel release];  
  15.     [_speedLabel release];  
  16.     [_degreeLabel release];  
  17.     [_nameLabel release];  
  18.     [super dealloc];  
  19. }  
  20. @end  

BusPointCell.xib




Step7

自定義的CalloutView都準備妥當,現在就是要實現他們的部分了,簡單說一下原理,在didSelectAnnotationView函數裏創建並添加calloutview的annotation(CalloutMapAnnotation),然後在viewForAnnotation函數內實例化要顯示的calloutview(CallOutAnnotationView)


首先聲明一個局部變量CalloutMapAnnotation *_calloutMapAnnotation;

在didSelectAnnotationView函數內添加如下代碼:

  1. //CustomPointAnnotation 是自定義的marker標註點,通過這個來得到添加marker時設置的pointCalloutInfo屬性  
  2. CustomPointAnnotation *annn = (CustomPointAnnotation*)view.annotation;  
  3.   
  4.   
  5. if ([view.annotation isKindOfClass:[CustomPointAnnotation class]]) {  
  6.       
  7.     //如果點到了這個marker點,什麼也不做  
  8.     if (_calloutMapAnnotation.coordinate.latitude == view.annotation.coordinate.latitude&&  
  9.         _calloutMapAnnotation.coordinate.longitude == view.annotation.coordinate.longitude) {  
  10.         return;  
  11.     }  
  12.     //如果當前顯示着calloutview,又觸發了select方法,刪除這個calloutview annotation  
  13.     if (_calloutMapAnnotation) {  
  14.         [mapView removeAnnotation:_calloutMapAnnotation];  
  15.         _calloutMapAnnotation=nil;  
  16.           
  17.     }  
  18.     //創建搭載自定義calloutview的annotation  
  19.     _calloutMapAnnotation = [[[CalloutMapAnnotation alloc] initWithLatitude:view.annotation.coordinate.latitude andLongitude:view.annotation.coordinate.longitude] autorelease];  
  20.       
  21.     //把通過marker(ZNBCPointAnnotation)設置的pointCalloutInfo信息賦值給CalloutMapAnnotation  
  22.     _calloutMapAnnotation.locationInfo = annn.pointCalloutInfo;  
  23.       
  24.     [mapView addAnnotation:_calloutMapAnnotation];  
  25.   
  26.       
  27.       
  28.     [mapView setCenterCoordinate:view.annotation.coordinate animated:YES];  
  29.       
  30. }  


其次,要在viewForAnnotation裏創建我們的calloutview(CallOutAnnotationView),添加如下代碼:

  1. else if ([annotation isKindOfClass:[CalloutMapAnnotation class]]){  
  2.       
  3.     //此時annotation就是我們calloutview的annotation  
  4.     CalloutMapAnnotation *ann = (CalloutMapAnnotation*)annotation;  
  5.       
  6.     //如果可以重用  
  7.     CallOutAnnotationView *calloutannotationview = (CallOutAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"calloutview"];  
  8.       
  9.     //否則創建新的calloutView  
  10.     if (!calloutannotationview) {  
  11.         calloutannotationview = [[[CallOutAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"calloutview"] autorelease];  
  12.   
  13.         BusPointCell *cell = [[[NSBundle mainBundle] loadNibNamed:@"BusPointCell" owner:self options:nil] objectAtIndex:0];  
  14.           
  15.         [calloutannotationview.contentView addSubview:cell];  
  16.         calloutannotationview.busInfoView = cell;  
  17.     }  
  18.       
  19.     //開始設置添加marker時的賦值  
  20.     calloutannotationview.busInfoView.aliasLabel.text = [ann.locationInfo objectForKey:@"alias"];  
  21.     calloutannotationview.busInfoView.speedLabel.text = [ann.locationInfo objectForKey:@"speed"];  
  22.     calloutannotationview.busInfoView.degreeLabel.text =[ann.locationInfo objectForKey:@"degree"];  
  23.     calloutannotationview.busInfoView.nameLabel.text =  [ann.locationInfo objectForKey:@"name"];  
  24.       
  25.     return calloutannotationview;  
  26.       
  27. }  

[注意]在添加marker的判斷裏一定要設置markerannotation.canShowCallout =NO; 否則點擊marker會默認顯示系統的吹出框



Step8

calloutview的annotation也創建和添加了,接下來我們就設置一下marker對應吹出框的數據:



然後運行一下:



哈哈!搞定了吧,具體佈局可以自己通過code方式,或xib方式設計,目前點擊marker能顯示了,可是點擊其它區域還是無法顯示,所以我們在didDeselectAnnotationView方法裏還需要判斷一下,remove掉。


  1. -(void)mapView:(BMKMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view{  
  2.       
  3.     if (_calloutMapAnnotation&&![view isKindOfClass:[CallOutAnnotationView class]]) {  
  4.   
  5.         if (_calloutMapAnnotation.coordinate.latitude == view.annotation.coordinate.latitude&&  
  6.             _calloutMapAnnotation.coordinate.longitude == view.annotation.coordinate.longitude) {  
  7.             [mapView removeAnnotation:_calloutMapAnnotation];  
  8.             _calloutMapAnnotation = nil;  
  9.         }  
  10.           
  11.           
  12.     }  
  13.   
  14. }  



最後


之所以在顯示marker的annotation[本文爲CustomPointAnnotation]和顯示calloutview的annotation[本文爲CalloutMapAnnotation]裏各添加一個Dictionary,就是要在點擊時通過marker傳遞數據,添加時通過calloutview的annotation實例來設置每一個屬性的數據,已達到不同的maker,顯示不同的數據。


可能我的過程不是太清晰,自己仔細研究一下這三個函數和mapview自帶的callout調用過程,便會明白。



dmeo地址:http://download.csdn.net/detail/mad1989/5252037



2012年4月12日

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章