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

[cpp] view plaincopy

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

这个方法在点击地图marker时所触发(并显示callout)

[cpp] view plaincopy

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

这个方法在点击地图任意位置,相当于隐藏callout

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  1. #import "BMKAnnotationView.h"
  2. #import "BusPointCell.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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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函数内添加如下代码:

[cpp] view plaincopy

  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),添加如下代码:

[cpp] view plaincopy

  1. else if ([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掉。

[cpp] view plaincopy

  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调用过程,便会明白。

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

时间: 2024-10-02 05:11:15

ios 一步一步学会自定义地图吹出框(CalloutView)-->(百度地图,高德地图,google地图)的相关文章

自定义的弹出框列表适配器,类似于大众点评或美团

无意下载一个代码, 还不错,记录一下,说不定以后会用到.效果图如下 整体工程是引入了一个library,当然完全可以写到一个工程里面,如下截图 代码还是很好理解的, 下面贴出来,顺便又加了一些注释 1.MainActivity.java public class MainActivity extends ActionBarActivity { private PopupButton btn; private PopupButton btn2; private LayoutInflater inf

百度、高德开撕地图,手机地图第一争霸背后的阳谋

百度更多是C端用户使用频率更高,高德在B端用途更大,地图争霸,目标却是O2O 文/张书乐 这个周末,地图行业第一之争的舆论战打得火热.此次口水战的起源是高德单方面宣称"已成为行业第一",隔空喊话百度地图"不服来战",并宣称将允许第三方数据公司QuestMobile监测验证.百度地图第一时间发文回应,甩出三家权威机构数据和用户评测结果,吊打高德数据"掺水".不过,高德并未消停,仍放话坚持用第三方数据说话.面对友商如是挑衅,百度地图官方再度强势回应,

openlayers 3加载百度、高德、google瓦片地图

1.加载高德地图 //高德地图 var AMapLayer = new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}' }) }); 2.google地图 //google地图 var googleMapLayer = new ol.la

自定义PopupWindow弹出框(带有动画)

使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow 1 public class LostPopupWindow extends PopupWindow { 2 public Lost lost; 3 public void onLost(Lost lost){ 4 this.lost = lost; 5 } 6 private View conentView; 7 8 public View getConentView() { 9 return cone

iOS自定义提示弹出框(类似UIAlertView)

菜鸟一枚,大神勿喷.自己在牛刀小试的时候,发现系统的UIAlertView有点不喜欢,然后就自己自定义了一个UIAlertView,基本上实现了系统的UIAlertView,可以根据项目的需求修改UIAlertView的颜色.欢迎大神多多指导.不胜感激! 效果图: Paste_Image.png --------------直接贴代码---------不喜勿喷----------大神多多指导.不胜感激!------- #import <UIKit/UIKit.h> typedef void(^

swing自定义JDialog弹出框

第一次搞swing,自定义JDialog的例子较少,写下来备忘 ,对JDialog中的文本框进行了验证 package com.chauvet; import java.awt.Component;import java.awt.Dimension;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent

自定义PobUpwindow弹出框

在做分享这一块的时候,用到弹出框的功能,还必须得有点击返回键,同样可以撤销显示的弹出框,于是自己就动手写了一个,一切看代码: public class ShareBoardPicker implements OnClickListener { private Context mContext; private SharePobupWindow mPopupWindow;//分享框 private LinearLayout layout; private RelativeLayout mWeixi

自定义popup弹出框

ys_popup.css .ys-popup{     position:fixed;     top:0;     bottom:0;     left:0;     right:0;     display:none;     z-index: 99999;     background-color: rgba(0,0,0,0.4); } .ys-popup .ys-popup-content{     position:absolute;     left:30px;     right:

ionic中自定义底部弹出框

通用部分:<script>     MY_FUNCTION = (function() {        var upSheet = document.createElement('div');        upSheet.setAttribute('style', 'display:none;position:absolute;left:0px;top:0px;z-index: 9999;');        upSheet.style.width = document.documentE