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

ios 一步一步學會自定義地圖吹出框(CalloutView)-->(百度地圖,高德地圖,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. CustomPointAnnotation *pointAnnotation = [[CustomPointAnnotation alloc] init];  
  4. pointAnnotation.title = @"我是中國人";//因為繼承了BMKPointAnnotation,所以這些title,subtitle都可以設定
  5. pointAnnotation.subtitle = @"我愛自己的祖國";  
  6. pointAnnotation.coordinate = center;  
  7. [mymapview addAnnotation:pointAnnotation];  
  8. [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. @interface CalloutMapAnnotation : NSObject<BMKAnnotation>  
  4. @property (nonatomic) CLLocationDegrees latitude;  
  5. @property (nonatomic) CLLocationDegrees longitude;  
  6. @property(retain,nonatomic) NSDictionary *locationInfo;//callout吹出框要顯示的各資訊
  7. - (id)initWithLatitude:(CLLocationDegrees)lat andLongitude:(CLLocationDegrees)lon;  
  8. @end  

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

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

Step6

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

[注意:繼承自BMKAnnotationView]

CallOutAnnotationView.h

  1. #import "BMKAnnotationView.h"
  2. #import "BusPoiltCell.h"
  3. @interface CallOutAnnotationView : BMKAnnotationView  
  4. @property(nonatomic,retain) UIView *contentView;  
  5. //新增一個UIView
  6. @property(nonatomic,retain) BusPointCell *busInfoView;//在建立calloutView Annotation時,把contentView add的 subview賦值給businfoView
  7. @end  

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

CallOutAnnotationView.m

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

BusPointCell.h

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

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

BusPointCell.m
  1. #import "BusPointCell.h"
  2. @implementation BusPointCell  
  3. - (id)initWithFrame:(CGRect)frame  
  4. {  
  5.     self = [super initWithFrame:frame];  
  6.     if (self) {  
  7.     }  
  8.     return self;  
  9. }  
  10. - (void)dealloc {  
  11.     [_aliasLabel release];  
  12.     [_speedLabel release];  
  13.     [_degreeLabel release];  
  14.     [_nameLabel release];  
  15.     [super dealloc];  
  16. }  
  17. @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. if ([view.annotation isKindOfClass:[CustomPointAnnotation class]]) {  
  4.     //如果點到了這個marker點,什麼也不做
  5.     if (_calloutMapAnnotation.coordinate.latitude == view.annotation.coordinate.latitude&&  
  6.         _calloutMapAnnotation.coordinate.longitude == view.annotation.coordinate.longitude) {  
  7.         return;  
  8.     }  
  9.     //如果當前顯示著calloutview,又觸發了select方法,刪除這個calloutview annotation
  10.     if (_calloutMapAnnotation) {  
  11.         [mapView removeAnnotation:_calloutMapAnnotation];  
  12.         _calloutMapAnnotation=nil;  
  13.     }  
  14.     //建立搭載自定義calloutview的annotation
  15.     _calloutMapAnnotation = [[[CalloutMapAnnotation alloc] initWithLatitude:view.annotation.coordinate.latitude andLongitude:view.annotation.coordinate.longitude] autorelease];  
  16.     //把通過marker(ZNBCPointAnnotation)設定的pointCalloutInfo資訊賦值給CalloutMapAnnotation
  17.     _calloutMapAnnotation.locationInfo = annn.pointCalloutInfo;  
  18.     [mapView addAnnotation:_calloutMapAnnotation];  
  19.     [mapView setCenterCoordinate:view.annotation.coordinate animated:YES];  
  20. }  


其次,要在viewForAnnotation裡建立我們的calloutview(CallOutAnnotationView),新增如下程式碼:

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

[注意]在新增marker的判斷裡一定要設定markerannotation.canShowCallout =NO; 否則點選marker會預設顯示系統的吹出框

Step8

calloutview的annotation也建立和添加了,接下來我們就設定一下marker對應吹出框的資料:


然後執行一下:


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

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


最後

之所以在顯示marker的annotation[本文為CustomPointAnnotation]和顯示calloutview的annotation[本文為CalloutMapAnnotation]裡各新增一個Dictionary,就是要在點選時通過marker傳遞資料,新增時通過calloutview的annotation例項來設定每一個屬性的資料,已達到不同的maker,顯示不同的資料。

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


相關推薦

ios 學會定義地圖(CalloutView)-->(地圖地圖,google地圖)

前言 在ios上邊使用地相簿的同學肯定遇到過這樣的問題:吹出框只能設定title和subtitle和左右的view,不管是百度地圖還是高德地圖還是自帶的google地圖,只提供了這四個屬性,如果想新增更多的view,只能自定義。可是,類庫只能看到.h檔案,.m都看不

ios 學會定義地圖(CalloutView)-->(地圖地圖,google地圖)...

前言 在ios上邊使用地相簿的同學肯定遇到過這樣的問題:吹出框只能設定title和subtitle和左右的view,不管是百度地圖還是高德地圖還是自帶的google地圖,只提供了這四個屬性,如果想新增更多的view,只能自定義。可是,類庫只能看到.h檔案,.m都看不到

Android中定義底部彈ButtomDialog

先看看效果和你要的是否一樣 一 、先來配置自定義控制元件需要的資源。 1.在res資料夾下建立一個anim資料夾並建立兩個slide_in_bottom.xml、slide_out_bottom.xml檔案,負責彈框進出動畫。 <?xml version="1.0" enco

Unity定義訊息彈

參考下面兩個連結: 【Unity技巧】自定義訊息框(彈出框) http://blog.csdn.net/candycat1992/article/details/24984347 這裡面的本地化文字方法好像不適合NGUI高版本,可以使用下面連結的本地化方法。 NGUI本地

如何實現一個定義的彈

.ewb-mesgbox { position: relative; width: 500px; background-color: #fff; } .ewb-msgbox-hd { height: 60px; line-height

Android定義下拉重新整理動畫--仿外賣下拉重新整理

好久沒寫部落格了,小編之前一段時間一直在找工作,從天津來到了我們的大帝都,感覺還不錯。好了廢話不多說了,開始我們今天的主題吧。現如今的APP各式各樣,同樣也帶來了各種需求,一個下拉重新整理都能玩出花樣了,前兩天訂飯的時候不經意間看到了“百度外賣”的下拉重新整理,今天

建立你的輸入法高階定義短語(以PC版拼音輸入法為例)

由於我在Markdown寫作以及java程式碼中經常用到某些固定的短語,為此製作了自己的短語庫。比如通過輸入“sjx”就可以輸出“▲”、輸入“itable”就可以建立一個HTML語法的表格: PC版百度拼音輸入法的自定義短語功能的位置:

定義PopupWindow彈(帶有動畫)和呼叫相簿相機佈局

使用PopupWindow來實現彈出框,並且帶有動畫效果 效果一:(第二張圖)首先自定義PopupWindow public class LostPopupWindow extends PopupWindow { public Lost lost;

地圖定義關閉彈陰影

    當我們自定義百度地圖彈出框時,一般也會自定義它的關閉按鈕。這樣本來也沒什麼問題,可是會在關閉了彈出框以後,在地圖上出來一個投射的陰影。這就很尷尬了,這會讓設計師很不爽,所以我們需要把這個陰影也去掉,我最開始做的是$('.BMap_shadow').html('');這

android 定義登陸彈

 從本節將開始闡述一個完整系統Demo,從登陸--主頁--查詢展示具體詳情...這節是一個自定義登陸彈出框,接著上一節的logo全屏頁面之後將出現一個全屏的ImagView,點選彈出自定義登陸框,輸入賬號密碼提交,彈出

vivado 生成自己的定義IP核

基於AXI-Lite的使用者自定義IP核設計 這裡以使用者自定義led_ip為例: 1.建立工程 和設計一過程一樣 這樣我們就進入了主介面 2.建立IP Tools –》Create and Package IP 來到IP建立歡迎介面:Next 接下來

帶你實現定義圓形進度條(詳解)

        每次看到別人做出炫酷的都會想,這個應該很難吧?這是心理上先入為主的就這麼認為了,其實實現很簡單,下面一步一步的詳細剖析自定義圓形進度條的步驟。 首先看效果圖: 篇幅有點長,耐心看完肯定get新技能。 看每一個檢視都包含了些什麼。 最

iOS開發之窺探UICollectionViewController(四) --款功能強大的定義瀑布流

在上一篇部落格中,自定義瀑布流的列數,Cell的外邊距,Cell的最大以及最小高度是在我們的佈局檔案中是寫死的,換句話說也就是不可配置的。為了循序漸進,由淺入深呢,上篇部落格暫且那麼寫。不過那樣寫太過死板,本來使用起來比較靈活的自定義佈局,如果把其配置引數給寫死了,就相當於在籠

定義GroupingComparator -- 求筆訂單中成交金額最大的筆交易

程式碼地址: https://gitee.com/tanghongping/hadoopMapReduce/tree/master/src/com/thp/bigdata/secondarySort 訂單id 商品id 成交金額

[Swift通天遁地]、超級工具-(17)定義的CVCalendar日曆

本文將演示另一款第三方的日曆類庫。 首先確保在專案中已經安裝了所需的第三方庫。 點選【Podfile】,檢視安裝配置檔案。 1 platform :ios, '12.0' 2 use_frameworks! 3 4 target 'DemoApp' do 5 source 'ht

通用資料級許可權控制解決方案的實現():Cube中的定義DLL

BI資料分析是目前企業的熱門應用,而對企業來說,進行許可權控制是必須而且非常重要的,尤其是作為決策用的企業報表。在BI解決方案中,許可權控制又分為2種:一種是報表級許可權控制,這型別許可權控制沒有什麼好講的,報表系統都本身就支援了。另一種比較複雜的就是資料級許可權控制,所

AndroidTV開發(十)Android Tv Launcher定義RecyclerView

前言 Android TV Launcher頁在RecyclerView出來之前大家用GridView去實現(本人的FocusView)。TV開發有五向鍵的監聽,遙控器hover監聽,點選事件等。用GridView去處理焦點是有一定挑戰性的,往往會出現不可預料焦點錯亂

Android定義控制元件開發系列()——第一次動手做定義控制元件

        Android系統提供的控制元件多種多樣,以至於很多初學者經常忘了還有這樣那樣的控制元件沒用過甚至沒聽過。儘管如此,但是系統控制元件大多比較死板,而且不夠美觀,很多多樣化的顯示或是互動

Android-UI佈局---RecyclerView學習()在介面卡中定義長按和點選事件

該系列文章  如果想全方面學習,建議參考這個大牛的文章,寫的真可以。 地址:http://blog.csdn.net/lmj623565791/article/details/45059587 因為RecyclerView沒有點選、長按事件,需要自己寫 實現的方式比較多,

文說透WordPress的定義文章類型

開發 高級功能 什麽 不一定 聚焦 列表 設計 函數 區分 從 2004 年的 1.0 版本算起,WordPress 在 14 年間已經叠代開發到了 5.x 版。如果說這中間哪個版本是一個質的提升的話,那應該算是 2010 年發布的代號為 Thelonious 的 3.0