• 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.   
    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

  • 相关阅读:
    02 : python 基础语法,流程控制语句
    10 : mysql 主从复制
    docker Dockerfile 参数讲解
    docker service 创建swarm节点服务
    docker image 删除未使用的镜像
    docker config 创建配置文件
    docker wait 命令使用
    docker update 更新容器信息
    docker top 命令使用
    docker tag 修改镜像的标枪
  • 原文地址:https://www.cnblogs.com/ejllen/p/4207915.html
Copyright © 2020-2023  润新知