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
- #pragma mark
- #pragma mark - BMKMapview delegate
- -(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation;
这个方法在点击地图marker时所触发(并显示callout)
- -(void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view;
这个方法在点击地图任意位置,相当于隐藏callout
- -(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
- //添加自定义Annotation
- CLLocationCoordinate2D center = {39.91669,116.39716};
- CustomPointAnnotation *pointAnnotation = [[CustomPointAnnotation alloc] init];
- pointAnnotation.title = @"我是中国人";//因为继承了BMKPointAnnotation,所以这些title,subtitle都可以设置
- pointAnnotation.subtitle = @"我爱自己的祖国";
- pointAnnotation.coordinate = center;
- [mymapview addAnnotation:pointAnnotation];
- [pointAnnotation release];
在viewForanntion里,由于我对marker没太大要求,直接使用了BMKPinAnnotationView(图钉),简单设置image属性为自己需要的图标,如下所示:
展示一个效果图:
显然CalloutView只能设置title和subtitle,无法满足我们的要求,那么继续下一步。
Step5
创建一个(自定义的CalloutView)的Annotation,相当于显示calloutView的annotation。
[注意] 继承自NSObject<BMKAnnotation>
CalloutMapAnnotation.h
- #import <Foundation/Foundation.h>
- #import "BMapKit.h"
- @interface CalloutMapAnnotation : NSObject<BMKAnnotation>
- @property (nonatomic) CLLocationDegrees latitude;
- @property (nonatomic) CLLocationDegrees longitude;
- @property(retain,nonatomic) NSDictionary *locationInfo;//callout吹出框要显示的各信息
- - (id)initWithLatitude:(CLLocationDegrees)lat andLongitude:(CLLocationDegrees)lon;
- @end
CalloutMapAnnotation.m
- #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
这里设置了经纬度的属性,和一个init初始化经纬度的方法(经纬度=marker的经纬度),同样添加了一个Dictionary的属性,为了传递在CalloutView上内容的赋值,继续。
Step6
这一步我们创建自定义的View,想要什么布局就写什么样的布局,想要多少属性就加多少属性,这里我使用了code方式画了一个contentView,里面的子view使用Xib方式创建。
[注意:继承自BMKAnnotationView]
CallOutAnnotationView.h
- #import "BMKAnnotationView.h"
- #import "BusPointCell.h"
- @interface CallOutAnnotationView : BMKAnnotationView
- @property(nonatomic,retain) UIView *contentView;
- //添加一个UIView
- @property(nonatomic,retain) BusPointCell *busInfoView;//在创建calloutView Annotation时,把contentView add的 subview赋值给businfoView
- @end
BusPointCell是ContentView里的subview,这个view就是显示各个组件,并赋不同的值
CallOutAnnotationView.m
- #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),
- // midy = CGRectGetMidY(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
- #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
- #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都准备妥当,现在就是要实现他们的部分了,简单说一下原理,在didSelectAnnotationView函数里创建并添加calloutview的annotation(CalloutMapAnnotation),然后在viewForAnnotation函数内实例化要显示的calloutview(CallOutAnnotationView)
首先声明一个局部变量CalloutMapAnnotation *_calloutMapAnnotation;
在didSelectAnnotationView函数内添加如下代码:
- //CustomPointAnnotation 是自定义的marker标注点,通过这个来得到添加marker时设置的pointCalloutInfo属性
- CustomPointAnnotation *annn = (CustomPointAnnotation*)view.annotation;
- if ([view.annotation isKindOfClass:[CustomPointAnnotation class]]) {
- //如果点到了这个marker点,什么也不做
- if (_calloutMapAnnotation.coordinate.latitude == view.annotation.coordinate.latitude&&
- _calloutMapAnnotation.coordinate.longitude == view.annotation.coordinate.longitude) {
- return;
- }
- //如果当前显示着calloutview,又触发了select方法,删除这个calloutview annotation
- if (_calloutMapAnnotation) {
- [mapView removeAnnotation:_calloutMapAnnotation];
- _calloutMapAnnotation=nil;
- }
- //创建搭载自定义calloutview的annotation
- _calloutMapAnnotation = [[[CalloutMapAnnotation alloc] initWithLatitude:view.annotation.coordinate.latitude andLongitude:view.annotation.coordinate.longitude] autorelease];
- //把通过marker(ZNBCPointAnnotation)设置的pointCalloutInfo信息赋值给CalloutMapAnnotation
- _calloutMapAnnotation.locationInfo = annn.pointCalloutInfo;
- [mapView addAnnotation:_calloutMapAnnotation];
- [mapView setCenterCoordinate:view.annotation.coordinate animated:YES];
- }
其次,要在viewForAnnotation里创建我们的calloutview(CallOutAnnotationView),添加如下代码:
- else if ([annotation isKindOfClass:[CalloutMapAnnotation class]]){
- //此时annotation就是我们calloutview的annotation
- CalloutMapAnnotation *ann = (CalloutMapAnnotation*)annotation;
- //如果可以重用
- CallOutAnnotationView *calloutannotationview = (CallOutAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"calloutview"];
- //否则创建新的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;
- }
- //开始设置添加marker时的赋值
- 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的判断里一定要设置markerannotation.canShowCallout =NO;
否则点击marker会默认显示系统的吹出框
Step8
calloutview的annotation也创建和添加了,接下来我们就设置一下marker对应吹出框的数据:
然后运行一下:
哈哈!搞定了吧,具体布局可以自己通过code方式,或xib方式设计,目前点击marker能显示了,可是点击其它区域还是无法显示,所以我们在didDeselectAnnotationView方法里还需要判断一下,remove掉。
- -(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]里各添加一个Dictionary,就是要在点击时通过marker传递数据,添加时通过calloutview的annotation实例来设置每一个属性的数据,已达到不同的maker,显示不同的数据。
可能我的过程不是太清晰,自己仔细研究一下这三个函数和mapview自带的callout调用过程,便会明白。
dmeo地址:http://download.csdn.net/detail/mad1989/5252037
ios 一步一步学会自定义地图吹出框(CalloutView)-->(百度地图,高德地图,google地图)的更多相关文章
- UIPresentationController - iOS自定义模态弹出框
参考: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Definin ...
- 自定义PopupWindow弹出框(带有动画)
使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow public class LostPopupWindow extends PopupWindow { pub ...
- iOS自定义提示弹出框(类似UIAlertView)
菜鸟一枚,大神勿喷.自己在牛刀小试的时候,发现系统的UIAlertView有点不喜欢,然后就自己自定义了一个UIAlertView,基本上实现了系统的UIAlertView,可以根据项目的需求修改UI ...
- swing自定义JDialog弹出框
第一次搞swing,自定义JDialog的例子较少,写下来备忘 ,对JDialog中的文本框进行了验证 package com.chauvet; import java.awt.Component; ...
- vue中点击屏幕其他区域关闭自定义div弹出框
直接上代码: mounted: function () { let that = this; $(document).on('click', function (e) { let dom = $('. ...
- 自定义ionic弹出框
<img width="64" height="64" src="img/timg.jpg" style="border-r ...
- 微信小程序之自定义底部弹出框动画
最近做小程序时,会经常用到各种弹框.直接做显示和隐藏虽然也能达到效果,但是体验性太差,也比较简单粗暴.想要美美地玩,添加点动画还是非常有必要的.下面做一个底部上滑的弹框. wxml <view ...
- 自定义一个类似UIAlertView的弹出框
这个是和UIAlertView类似,但是可以自定义view的样式废话不多说,上代码: 首先第一步:创建一个继承自View的类如: #import <UIKit/UIKit.h> @clas ...
- android自定义弹出框样式实现
前言: 做项目时,感觉Android自带的弹出框样式比较丑,很多应用都是自己做的弹出框,这里也试着自己做了一个. 废话不说先上图片: 实现机制 1.先自定义一个弹出框的样式 2.自己实现CustomD ...
随机推荐
- Asp.net IIS Express 无法启动 解决办法
http://www.mamicode.com/info-detail-1893424.html 一 .其他项目都可以,就这么一个不行 用记事本或者其他什么文本编辑器,打开项目的.csproj文件,定 ...
- 关于linux命令ssh的总结
因为项目计算量比较大,需要将任务分布到多台电脑上面运行,因为对于分布式概念不熟,就想到了linux最简单的ssh协议,远程控制其他电脑,然后写shell脚本统一在所有电脑上运行程序.(我的操作系统为U ...
- sql优化原则与技巧
加快sql查询是非常重要的技巧,简单来说加快sql查询的方式有以下几种:一.索引的引用 1.索引一般可以加速数据的检索速度,加速表与表之间的链接,提高性能,所以在对海量数据进行处理时,考虑到信息量比较 ...
- 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD)
一些小的C/S项目(winform.WPF等),因需要访问操作数据库,但又不能把DB连接配置在客户端上,原因有很多,可能是DB连接无法直接访问,或客户端不想安装各种DB访问组件,或DB连接不想暴露在客 ...
- vue2.0 正确理解Vue.nextTick()的用途
什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 获取更新后的DOM,言外之意就是DOM更新 ...
- python3之shutil高级文件操作
1.shutil高级文件操作模块 shutil模块提供了大量的文件的高级操作.特别针对文件拷贝和删除,主要功能为目录和文件操作以及压缩操作.对单个文件的操作也可参见os模块. 2.shutil模块的拷 ...
- Windows程序设计笔记(二) 关于编写简单窗口程序中的几点疑惑
在编写窗口程序时主要是5个步骤,创建窗口类.注册窗口类.创建窗口.显示窗口.消息环的编写.对于这5个步骤为何要这样写,当初我不是太理解,学习到现在有些问题我基本上已经找到了答案,同时对于Windows ...
- Mixin Network Whitepaper
[译] Mixin Network(网络) 看到了李笑来老师牵头的 Mixin, 也仔细查看了 Mixin 白皮书 ,决定翻译过来 构造一个最大的,并且对开发者友好的移动区块链网络,连接现有的所有区块 ...
- python查询完结篇
0x00 网上找一个查询网站,然后自己写的一个脚本 0x01 代码送上: import requests import time from bs4 import BeautifulSoup strat ...
- 移动端APP列表点透事件处理方法
关于点透事件这里不再赘述,如果不清楚的可以上网搜一搜,或者看小火柴的这篇文章. 这里是自己在做移动端时,在列表滑动的时候,遇到的点透问题.出现这个问题的来由是因为在转场的时候,各个手机的转场效果不一样 ...