前言
在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)annotation;
-(void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view;
-(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];
展示一个效果图:
显然CalloutView只能设置title和subtitle,无法满足我们的要求,那么继续下一步。
Step5
创建一个(自定义的CalloutView)的Annotation,相当于显示calloutView的annotation。
[注意] 继承自NSObject<BMKAnnotation>
CalloutMapAnnotation.h
#import
#import "BMapKit.h"
@interface CalloutMapAnnotation : NSObject
@property (nonatomic) CLLocationDegrees latitude;
@property (nonatomic) CLLocationDegrees longitude;
@property(retain,nonatomic) NSDictionary *locationInfo;//callout吹出框要显示的各信息
- (id)initWithLatitude:(CLLocationDegrees)lat andLongitude:(CLLocationDegrees)lon;
@end
#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
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
CallOutAnnotationView.m
#import "CallOutAnnotationView.h"
#import
#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)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
@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
#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
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;
}
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
2012年4月12日
版权声明:本文为博主原创文章,未经博主允许不得转载。