定位和地圖

為了使用iOS中的地圖和定位功能糠爬,我們需要使用Core Location和Map Kit拷橘,分別用于地理定位和地圖展示。

1.Core Location

1.獲取權限

要開始定位服務的第一步就是獲取用戶權限,在iOS8之前,獲取位置服務權限是隱式的屑墨。你只要初始化CLLocationManager并設置它的delegate為它的owner岸霹,當你開始調用startUpdatingLocation進行定位時疾层,如果應用程序還沒有被許可或者之前被拒絕了的話,會觸發(fā)系統(tǒng)彈出提示框向用戶獲取位置服務的授權:

@interface ViewController ()<CLLocationManagerDelegate>{
    CLLocationManager *locationmanager;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self.locationManager startUpdatingLocation];
}

即讓 CLLocationManager 取得最新的位置的這個操作會讓系統(tǒng)彈出是否允許位置服務的提示贡避。

在 iOS 8痛黎,取得權限和使用位置服務分成兩個動作了。分別用兩個不同的方法取得權限:requestWhenInUseAuthorization 和 requestAlwaysAuthorization刮吧。前者只能讓應用在使用的時候有權獲取位置數據湖饱;后者會得到跟上面講的一樣獲得后臺位置服務。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    if (IS_IOS8) {
        locationmanager = [[CLLocationManager alloc] init];
        [locationmanager requestAlwaysAuthorization];
        [locationmanager requestWhenInUseAuthorization];
        locationmanager.delegate = self;
    }    
}

注意你需要判斷設備是不是iOS8才可以使用上面的方法杀捻,否則locationmanager不響應相應的方法會造成應用奔潰:

#define IS_IOS8 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8)

或者也可以這樣:

if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [self.locationManager requestWhenInUseAuthorization];
    }

在iOS8之前井厌,你可以選擇性在 Info.plist 中包含 'NSLocationUsageDescription' 的關鍵字。這個值是一個純文本的字符串,向用戶說明了應用要使用位置服務的目的〗銎停現(xiàn)在這個值被拆分成了兩個不同的關鍵字:NSLocationWhenInUseUsageDescription和 NSLocationAlwaysUsageDescription,而且是必填的器赞;如果你不添加對應的關鍵字就去調用 requestWhenInUseAuthorization或requestAlwaysAuthorization,那么將不會有任何的彈出提示給用戶。

那么下面這樣設置是對的嗎墓拜?

注意不能像上圖那樣設置為兩個BOOL值港柜,那樣會導致用戶在隱私設置頁面打開你的應用的定位服務設置后使得設置頁面奔潰。因為上面兩個關鍵字對于的必須是字符串咳榜,不能是bool值夏醉。

下面這樣才是正確的(我以前就在這里被其它文章誤導所以使用bool值)

注意授權彈出框會只顯示一次。在CLLocationManager.authorizationStatus()返回除NotDetermined之外的值之后贿衍,不管調用requestWhenInUseAuthorization()或requestAlwaysAuthorization()都不會有一個 UIAlertController 顯示出來了授舟。在用戶最初的選擇之后,唯一改變授權的方式是到隱私設置中設置贸辈。

為了快速到達隱私設置頁面释树,Apple引入UIApplicationOpenSettingsURLString,它存儲了一個 URL 用來打開當前應用在 Settings.app 對應的頁面擎淤。如下:

- (IBAction)setting:(id)sender {
    if (&UIApplicationOpenSettingsURLString != NULL) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }
}

2.CLLocationManager

如上講的奢啥,CLLocationManager是管理我們的定位服務的,CLLocationManager包括下列屬性:

desiredAccuracy是指定位精度嘴拢,它是個枚舉類型:

kCLLocationAccuracyBest:最精確定位
CLLocationAccuracy kCLLocationAccuracyNearestTenMeters:十米誤差范圍
kCLLocationAccuracyHundredMeters:百米誤差范圍
kCLLocationAccuracyKilometer:千米誤差范圍
kCLLocationAccuracyThreeKilometers:三千米誤差范圍

distanceFilter是位置信息更新最小距離桩盲,只有移動大于這個距離才更新位置信息,默認為kCLDistanceFilterNone:不進行距離限制席吴。

stopUpdatingLocation:停止定位追蹤
startUpdatingHeading:開始導航方向追蹤
stopUpdatingHeading:停止導航方向追蹤
startMonitoringForRegion::開始對某個區(qū)域進行定位追蹤赌结,開始對某個區(qū)域進行定位后。如果用戶進入或者走出某個區(qū)域會調用

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region

stopMonitoringForRegion:停止對某個區(qū)域進行定位追蹤
requestWhenInUseAuthorization:請求獲得應用使用時的定位服務授權
requestAlwaysAuthorization:請求獲得應用一直使用定位服務授權孝冒,

我們從一個例子開始講起柬姚,先完成一些需要的工作:

記住,你需要把MapKit framework加入到項目中. (Control + Click Frameworks folder -> Add -> Existing Frameworks)

為了分離控制器庄涡,我們新建一個NSObject子類量承,

MyLocation.h

#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>

@interface MyLocation : NSObject<CLLocationManagerDelegate,UIAlertViewDelegate>

@end

MyLocation.m

-(void)startLocation
{
    if([CLLocationManager locationServicesEnabled] && [CLLocationManager authorizationStatus] != kCLAuthorizationStatusDenied) {
        _manager=[[CLLocationManager alloc]init];
        _manager.delegate=self;
        _manager.desiredAccuracy = kCLLocationAccuracyBest;
        _manager.distanceFilter=100;
        [_manager requestAlwaysAuthorization];
        [_manager startUpdatingLocation];
    }
    else {
        UIAlertView *alvertView=[[UIAlertView alloc]initWithTitle:@"提示" message:@"你還未開啟定位服務" delegate:nil cancelButtonTitle:@"設置" otherButtonTitles: @"取消", nil];
        alvertView.delegate = self;
        [alvertView show];
    }    
}

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if (&UIApplicationOpenSettingsURLString != NULL) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
    [self stopLocation];
}

-(void)stopLocation{
    _manager = nil;
}

需要注意幾點:

1).定位頻率和定位精度并不是越精確越好,需要視實際情況而定穴店,因為越精確越耗性能撕捍,也就越費電。

2).定位成功后會根據設置情況頻繁調用

 -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations

方法泣洞,這個方法返回一組地理位置對象數組忧风,每個元素一個CLLocation代表地理位置信息(包含經度、緯度球凰、海報狮腿、行走速度等信息)该窗,之所以返回數組是因為有些時候一個位置點可能包含多個位置。

3).使用完定位服務后如果不需要實時監(jiān)控應該立即關閉定位服務以節(jié)省資源蚤霞。

4).除了提供定位功能酗失,CLLocationManager還可以調用startMonitoringForRegion:方法對指定區(qū)域進行監(jiān)控。

3.實例

對于CLLocation對象昧绣,適合使用單例來創(chuàng)建它

+ (MyLocation *)shareLocation{
    static dispatch_once_t pred = 0;
    __strong static id _sharedObject = nil;
    dispatch_once(&pred, ^{
        _sharedObject = [[self alloc] init];
    });
    return _sharedObject;
}

以下協(xié)議會在startUpdatingLocation后被調用规肴,我們可以在這個方法中得到我們需要的信息:

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    CLLocation *loc = [locations firstObject];
    self.lastCoordinate = loc.coordinate;
}

我們通過Block傳遞消息到控制類:

typedef void (^locationCorrrdinate)(CLLocationCoordinate2D locationCoordinate);
...省略
-(void)getLocationMessageCoordinate2D:(locationCorrrdinate)locationCoordinate{
    [self startLocation];
    locationCoordinate(self.lastCoordinate);
}

ViewController.m

- (IBAction)getLocationMessage:(UIButton *)sender {
    __block __weak ViewController *wself = self;
    
    [[MyLocation shareLocation]getLocationMessageCoordinate2D:^(CLLocationCoordinate2D locationCoordinate) {
        NSLog(@"緯度--%f 經度----%f",locationCoordinate.latitude,locationCoordinate.longitude);
        NSString  *str = [NSString stringWithFormat:@"%f,%f",locationCoordinate.latitude,locationCoordinate.longitude];
        wself.coordinateLabel.text = str;
    }];
}

現(xiàn)在我們運行應用,現(xiàn)在你按下location夜畴,你可以看到:

正如我們所期望的拖刃,如果你點擊不允許之后,繼續(xù)點擊location按鈕贪绘,你會看到:

點擊設置選項可以快速跳轉到地理位置服務設置頁面兑牡。

不過有一點問題,在我們允許使用地理位置服務后税灌,最初我們按下按鈕的時候均函,顯示為0,再按下才有:

2015-03-16 22:19:06.528 LocationDemo[2420:1034611] 緯度--0.000000 經度----0.000000
2015-03-16 22:19:09.445 LocationDemo[2420:1034611] 緯度--23.046266 經度----113.386919

因為設備獲取經緯度需要一定的時間菱涤,最初按下的時候還沒有獲取到經緯度值苞也。下面的代碼是解決辦法。

@property (nonatomic,strong)locationCorrrdinate locationCoordinateBlock;
...省略
-(void)getLocationMessageCoordinate2D:(locationCorrrdinate)locationCoordinate{
    self.locationCoordinateBlock = [locationCoordinate copy];
    [self startLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    CLLocation *loc = [locations firstObject];
    self.lastCoordinate = loc.coordinate;
    self.locationCoordinateBlock(loc.coordinate);
}

注意粘秆,我們之所以需要[locationCoordinate copy]是因為locationCoordinate是一個block如迟,分配在棧上,超過它的作用域之后block就不在了攻走。所以必須先copy到heap上殷勘,這樣就可以在之后的使用中正常訪問。我在這篇文章中曾詳細講過昔搂。

現(xiàn)在運行一切正常玲销。

3.地理位置編碼

為了將你上面得到的緯度和經度顯示為真實的地址,你需要使用地理位置編碼巩趁。
使用CLGeocoder可以完成“地理編碼”和“反地理編碼”
地理編碼:根據給定的地名痒玩,獲得具體的位置信息(比如經緯度淳附、地址的全稱等)
反地理編碼:根據給定的經緯度议慰,獲得具體的位置信息

地理編碼方法

- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler; 

反地理編碼方法

- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

我們先看反地理編碼方法,代碼如下:

-(void)getLocationMessageCoordinate2D:(locationCorrrdinate)locationCoordinate address:(address)address detailAddress:(address)detailAddress{
    self.locationCoordinateBlock = [locationCoordinate copy];
    self.addressBlock = [address copy];
    self.detailAddressBlock = [detailAddress copy];
    [self startLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    CLLocation *loc = [locations firstObject];
    self.coordinate = loc.coordinate;
    self.locationCoordinateBlock(loc.coordinate);
    
    CLGeocoder *geocoder = [[CLGeocoder alloc]init];

    [geocoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, NSError *error) {
        if (placemarks.count > 0) {
            CLPlacemark *placemark = [placemarks objectAtIndex:0];
            _address = [NSString stringWithFormat:@"%@%@",placemark.administrativeArea,placemark.locality];
            _detailAddress = [NSString stringWithFormat:@"%@",placemark.name];//詳細地址
            
             //        NSString *name=placemark.name;//地名
             //        NSString *thoroughfare=placemark.thoroughfare;//街道
             //        NSString *subThoroughfare=placemark.subThoroughfare; //街道相關信息奴曙,例如門牌等
             //        NSString *locality=placemark.locality; // 城市
             //        NSString *subLocality=placemark.subLocality; // 城市相關信息别凹,例如標志性建筑
             //        NSString *administrativeArea=placemark.administrativeArea; // 州
             //        NSString *subAdministrativeArea=placemark.subAdministrativeArea; //其他行政區(qū)域信息
             //        NSString *postalCode=placemark.postalCode; //郵編
             //        NSString *ISOcountryCode=placemark.ISOcountryCode; //國家編碼
             //        NSString *country=placemark.country; //國家
             //        NSString *inlandWater=placemark.inlandWater; //水源、湖泊
             //        NSString *ocean=placemark.ocean; // 海洋
             //        NSArray *areasOfInterest=placemark.areasOfInterest; //關聯(lián)的或利益相關的地標
        }
        if (self.addressBlock) {
            self.addressBlock(_address);
        }
        if (self.detailAddressBlock) {
            self.detailAddressBlock(_detailAddress);
        }
    }];
}

ViewController.m

- (IBAction)getAddress:(UIButton *)sender {
    __block __weak ViewController *wself = self;
    [[MyLocation shareLocation]getLocationMessageCoordinate2D:^(CLLocationCoordinate2D locationCoordinate) {
    } address:^(NSString *string) {
        wself.addressLabel.text = string;
    } detailAddress:^(NSString *string) {
    }];
}

- (IBAction)getDetailAddress:(UIButton *)sender {
    __block __weak ViewController *wself = self;
    [[MyLocation shareLocation]getLocationMessageCoordinate2D:^(CLLocationCoordinate2D locationCoordinate) {
        //
    } address:^(NSString *string) {
        //
    } detailAddress:^(NSString *string) {
        wself.detailAddressLabel.text = string;
    }];
}

好了洽糟,運行效果如下:

看一些地理編碼:根據給定的地名炉菲,獲得具體的位置信息堕战,如下:

-(void)geocodeAddress{
    CLGeocoder *geocoder = [[CLGeocoder alloc]init];
    [geocoder geocodeAddressString:@"廣東工業(yè)大學" completionHandler:^(NSArray *placemarks, NSError *error) {
        
        if (placemarks.count>0) {
            CLPlacemark *placemark=[placemarks firstObject];
            
            CLLocation *location=placemark.location;//位置
            CLRegion *region=placemark.region;//區(qū)域
            NSLog(@"位置:%@,區(qū)域:%@",location,region);
        }
    }];
}

打印結果如下;

2015-03-17 13:19:02.179 LocationDemo[2755:1160527] 位置:<+23.13312700,+113.29970500> +/- 100.00m (speed -1.00 mps / course -1.00) @ 15/3/17 中國標準時間下午1:19:02,區(qū)域:CLCircularRegion (identifier:'<+23.13312650,-66.70029500> radius 14867919.48', center:<+23.13312650,-66.70029500>, radius:14867919.48m)

2.MapKit

1.顯示我們所在的位置

上文我們已經可以查看我們的地理位置了,現(xiàn)在我們需要在地圖上顯示我們所處的位置拍霜。如下我們在地圖中顯示我們所處的區(qū)域嘱丢。

- (IBAction)getDetailAddress:(UIButton *)sender {
    __block __weak ViewController *wself = self;
    [[MyLocation shareLocation]getLocationMessageCoordinate2D:^(CLLocationCoordinate2D locationCoordinate) {
        MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(locationCoordinate, 2000, 2000);
        [self.mapView setRegion:region];

或:

MKCoordinateSpan span=MKCoordinateSpanMake(1, 1);
MKCoordinateRegion region=MKCoordinateRegionMake(locationCoordinate, span);
[_mapView setRegion:region animated:true];

然后我們地圖中顯示我們所處的位置,并添加一個大頭針祠饺,大頭針的位置是我們前文所獲取到的地理位置越驻。

_mapView.userTrackingMode=MKUserTrackingModeFollow;       
_mapView.mapType=MKMapTypeStandard;
//添加大頭針
MyAnnotation *annotation=[[MyAnnotation alloc]init];
annotation.title=@"位置";
annotation.subtitle=@"我獲取的位置";
annotation.coordinate=locationCoordinate;
[_mapView addAnnotation:annotation];

效果如下:

其中userTrackingMode:跟蹤類型,是一個枚舉:

MKUserTrackingModeNone :不進行用戶位置跟蹤道偷;
MKUserTrackingModeFollow :跟蹤用戶位置缀旁;
MKUserTrackingModeFollowWithHeading :跟蹤用戶位置并且跟蹤用戶前進方向;

如果你設置了用戶位置跟蹤勺鸦,你也可以省略上面第一步中的顯示用戶所在區(qū)域并巍,現(xiàn)在會自動顯示區(qū)域范圍并指定當前用戶位置為地圖中心點

我們還設置了mapType:地圖類型,同樣是一個枚舉:

MKMapTypeStandard :標準地圖换途,一般情況下使用此地圖即可滿足懊渡;
MKMapTypeSatellite :衛(wèi)星地圖;
MKMapTypeHybrid :混合地圖军拟,加載最慢比較消耗資源距贷;

你有沒有注意到上面的annotation即大頭針,它是一個我們自己定義的類吻谋。只要一個NSObject子中類實現(xiàn)MKAnnotation協(xié)議就可以作為一個大頭針忠蝗,通常會重寫協(xié)議中coordinate(標記位置)、title(標題)漓拾、subtitle(子標題)三個屬性阁最,然后在程序中創(chuàng)建大頭針對象并調用addAnnotation:方法添加大頭針即可。如下:

@interface MyAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

2.個性化大頭針

只要實現(xiàn)以下方法并在這個方法中定義一個大頭針視圖MKAnnotationView對象并設置相關屬性就可以改變默認大頭針的樣式骇两。

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;

方法可以返回一個大頭針視圖速种,MKAnnotationView常用屬性:

image:大頭針圖片
canShowCallout:點擊大頭針是否顯示標題、子標題內容等低千,需要設置為true配阵。
calloutOffset:點擊大頭針時彈出詳情信息視圖的偏移量
selected:是否被選中狀態(tài)
leftCalloutAccessoryView:彈出詳情左側視圖
rightCalloutAccessoryView:彈出詳情右側視圖

我們還可以使用它的子類:MKPinAnnotationView,該子類包含了兩個屬性示血,MKPinAnnotationColor pinColor和BOOL animatesDrop棋傍,分別可以設置大頭針顏色和是否顯示動畫效果(即大頭針從天而降的效果)
需要注意:

a.這個代理方法的調用時機:每當有大頭針顯示到系統(tǒng)可視界面中時就會調用此方法返回一個大頭針視圖放到界面中,同時當前系統(tǒng)位置標注(也就是地圖中藍色的位置點)也是一個大頭針难审,也會調用此方法瘫拣,因此處理大頭針視圖時需要區(qū)別對待。

b.我們使用dequeueReusableAnnotationViewWithIdentifier來緩存利用大頭針view告喊,類似于UITableView的代理方法麸拄;

c.自定義大頭針默認情況下不允許交互派昧,如果交互需要設置canShowCallout=true

d.如果代理方法返回nil則會使用默認大頭針視圖,需要根據情況設置拢切。

代碼如下:

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
    if([annotation isKindOfClass:[MyAnnotation class]]){
    static NSString *annotationIdentifier = @"AnnotationIdentifier";
    
    MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
    
    if (!pinView){
        pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
        [pinView setPinColor:MKPinAnnotationColorGreen];
        pinView.animatesDrop = YES;
        pinView.canShowCallout = YES;
        UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"a"]];
        pinView.leftCalloutAccessoryView = iconView;
    }
    else {
        pinView.annotation = annotation;
    }
    
    return pinView;
    }
    return nil;
}

效果如下:

你會不會覺得還不夠個性化蒂萎,下面我們繼續(xù)修改它的樣式。

注意上面的代碼中

if([annotation isKindOfClass:[MyAnnotation class]])

可得知淮椰,我們要加載其它樣式的MKAnnotationView岖是,我們同樣需要另外一個MKAnnotation,所以:

然后添加如下代碼記得不要忘了<MKAnnotation>:

@interface CustomAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

@property (nonatomic,strong) UIImage *image;
@property (nonatomic,strong) UIImage *icon;
@property (nonatomic,copy) NSString *detail;
@property (nonatomic,strong) UIImage *detailView;

和MKPinAnnotationView思路一樣实苞,我們需要新建一個MKAnnotationView的子類豺撑,如下:

然后我們把那堆討厭的 if (!pinView)~~ initWithAnnotation:annotation reuseIdentifier~~寫在自定義子類的類方法中,如下:

+(instancetype)layoutMyPinViewWithMapView:(MKMapView *)mapView{
    static NSString *annotationIdentifier = @"AnnotationIdentifier2";
    MyAnnotationView *pinView = (MyAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
    
    if (!pinView){
        pinView = [[self alloc]init];
    }
    return pinView;
}

初始化:

-(instancetype)init{
    if(self=[super init]){
        [self layoutUI];
    }
    return self;
}

-(void)layoutUI{
    _backgroundView=[[UIView alloc]init];
    _backgroundView.backgroundColor=[UIColor whiteColor];
    _iconView=[[UIImageView alloc]init];
    
    _detailLabel=[[UILabel alloc]init];
    _detailLabel.lineBreakMode=NSLineBreakByWordWrapping;
    _detailLabel.font=[UIFont systemFontOfSize:kDetailFontSize];
    
    _detailView=[[UIImageView alloc]init];
    
    [self addSubview:_backgroundView];
    [self addSubview:_iconView];
    [self addSubview:_detailLabel];
    [self addSubview:_detailView];
}

然后在annotation的set方法中進行布局:

-(void)setAnnotation:(CustomAnnotation *)annotation{
    [super setAnnotation:annotation];
    //根據模型調整布局
    _iconView.image=annotation.icon;
    _iconView.frame=CGRectMake(kSpacing, kSpacing, annotation.icon.size.width, annotation.icon.size.height);
    
    _detailLabel.text=annotation.detail;
    float detailWidth=150.0;
    CGSize detailSize= [annotation.detail boundingRectWithSize:CGSizeMake(detailWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kDetailFontSize]} context:nil].size;
    float detailX=CGRectGetMaxX(_iconView.frame)+kSpacing;
    _detailLabel.frame=CGRectMake(detailX, kSpacing, detailSize.width, detailSize.height);
    _detailView.image=annotation.detailView;
    _detailView.frame=CGRectMake(detailX, CGRectGetMaxY(_detailLabel.frame)+kSpacing, annotation.detailView.size.width, annotation.detailView.size.height);
    
    float backgroundWidth=CGRectGetMaxX(_detailLabel.frame)+kSpacing;
    float backgroundHeight=_iconView.frame.size.height+2*kSpacing;
    _backgroundView.frame=CGRectMake(0, 0, backgroundWidth*2, backgroundHeight);
    self.bounds=CGRectMake(0, 0, backgroundWidth, backgroundHeight+kViewOffset);
}

因為最開始顯示兩個大頭針的時候黔牵,我們是不需要我們自定義的MKAnnotationView子類聪轿,只需要把大頭針的image改了即可,所以我們像上面那樣寫:

        MyAnnotation *annotation=[[MyAnnotation alloc]init];
        annotation.title=@"位置1";
        annotation.subtitle=@"我獲取的位置";
        annotation.image=[UIImage imageNamed:@"locationImage1"];
        annotation.icon=[UIImage imageNamed:@"iconMark"];
        annotation.detail=@"哈哈";
        annotation.detailView=[UIImage imageNamed:@"iconStar"];
        annotation.coordinate=locationCoordinate;
        [wself.mapView addAnnotation:annotation];
        
        //添加我們自定義的大頭針
        CLLocationCoordinate2D location2=CLLocationCoordinate2DMake(locationCoordinate.latitude+0.002, locationCoordinate.longitude+0.002);
        MyAnnotation *annotation2 = [[MyAnnotation alloc]init];
        annotation2.title=@"位置2";
        annotation2.subtitle=@"我自定義的位置";
        annotation2.coordinate=location2;
        annotation2.image=[UIImage imageNamed:@"locationImage"];
        annotation2.icon=[UIImage imageNamed:@"iconMark"];
        annotation2.detail=@"呵呵";
        annotation2.detailView=[UIImage imageNamed:@"iconStar"];
        [wself.mapView addAnnotation:annotation2];

然后在下面的代理方法中:

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
    if([annotation isKindOfClass:[MyAnnotation class]]){
        static NSString *annotationIdentifier = @"AnnotationIdentifier";
        MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
        
        if (!pinView){
            pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
            //[pinView setPinColor:MKPinAnnotationColorGreen];
            //pinView.animatesDrop = YES;
            pinView.calloutOffset=CGPointMake(0, 1);
            UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"a"]];
            pinView.leftCalloutAccessoryView = iconView;
        }
        pinView.annotation = annotation;
        pinView.image=((MyAnnotation *)annotation).image;
        return pinView;
    }else if ([annotation isKindOfClass:[CustomAnnotation class]]){
        //
    }else{
        return nil;
    }
}

上面需要注意的是猾浦,不可以調用以下方法陆错,否則大頭針的image不顯示出來。

[pinView setPinColor:MKPinAnnotationColorGreen];
pinView.animatesDrop = YES;

而且上面這句

else if ([annotation isKindOfClass:[CustomAnnotation class]]){

目前不會調用到金赦,因為我們新建的兩個annotation都是MyAnnotation音瓷。而且需要注意的是,因為我們沒有設置pinView.canShowCallout = YES夹抗,所以canShowCallout默認為NO,點擊annotation后不會有詳情view顯示出來绳慎。所以我們需要在點擊annotation是加載我們自定義的CustomAnnotation

-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
    MyAnnotation *annotation=view.annotation;
    if ([view.annotation isKindOfClass:[MyAnnotation class]]) {
        CustomAnnotation *annotation1=[[CustomAnnotation alloc]init];
        annotation1.icon=annotation.icon;
        annotation1.detail=annotation.detail;
        annotation1.detailView=annotation.detailView;
        annotation1.coordinate=view.annotation.coordinate;
        [mapView addAnnotation:annotation1];
    }
}

然后在viewForAnnotation中加載我們的MyAnnotationView:

else if ([annotation isKindOfClass:[CustomAnnotation class]]){
        MyAnnotationView *myView=[MyAnnotationView layoutMyPinViewWithMapView:_mapView];
        myView.canShowCallout = NO;
        myView.annotation = annotation;
        return myView;
}

還需要在點擊除大頭針之外區(qū)域時移除大頭針:

#pragma mark 取消選中時觸發(fā)
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
    [self removeCustomAnnotation];
}

#pragma mark 移除所用自定義大頭針
-(void)removeCustomAnnotation{
    [_mapView.annotations enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[CustomAnnotation class]]) {
            [_mapView removeAnnotation:obj];
        }
    }];
}

好了,看效果:

你可以在這里下載到本文的代碼漠烧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末杏愤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子已脓,更是在濱河造成了極大的恐慌珊楼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件度液,死亡現(xiàn)場離奇詭異厕宗,居然都是意外死亡,警方通過查閱死者的電腦和手機堕担,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門已慢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人照宝,你說我怎么就攤上這事蛇受【淇” “怎么了厕鹃?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵兢仰,是天一觀的道長。 經常有香客問我剂碴,道長把将,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任察蹲,我火速辦了婚禮催训,結果婚禮上,老公的妹妹穿的比我還像新娘漫拭。我一直安慰自己亚兄,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布审胚。 她就那樣靜靜地躺著礼旅,像睡著了一般膳叨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痘系,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音临谱,去河邊找鬼奴璃。 笑死,一個胖子當著我的面吹牛抄课,可吹牛的內容都是我干的。 我是一名探鬼主播跟磨,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼抵拘,長吁一口氣:“原來是場噩夢啊……” “哼型豁!你這毒婦竟也來了尚蝌?” 一聲冷哼從身側響起充尉,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤驼侠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后苛预,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡碟渺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年苫拍,在試婚紗的時候發(fā)現(xiàn)自己被綠了旺隙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡垄提,死狀恐怖周拐,靈堂內的尸體忽然破棺而出妥粟,到底是詐尸還是另有隱情,我是刑警寧澤滩报,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布播急,位于F島的核電站,受9級特大地震影響可训,放射性物質發(fā)生泄漏。R本人自食惡果不足惜飞崖,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一蚜厉、第九天 我趴在偏房一處隱蔽的房頂上張望畜眨。 院中可真熱鬧术瓮,春花似錦、人聲如沸恬汁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至独郎,卻和暖如春枚赡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贪婉。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工谓松, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留践剂,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓优质,卻偏偏與公主長得像,于是被迫代替她去往敵國和親演怎。 傳聞我的和親對象是個殘疾皇子避乏,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容

  • 出自http://my.oschina.net/are1OfBlog/blog/420034 摘要 現(xiàn)在很多社交拍皮、...
    JJO閱讀 4,117評論 4 19
  • http://www.cnblogs.com/kenshincui/p/4125570.html 摘要: 現(xiàn)在很多...
    大崔老師閱讀 3,274評論 1 2
  • 現(xiàn)在手機軟件基本上都需要定位铆帽,比如打個的爹橱,附近的人,附近的餐廳等等愧驱,這些應用都需要定位和地圖服務组砚。 我這個人分不清...
    小白和小黑閱讀 1,930評論 4 16
  • 簡介: 在移動互聯(lián)網時代,移動app能解決用戶的很多生活瑣事手报,比如周邊:找餐館改化、找KTV、找電影院等等 導航:根據...
    冰鏡止水閱讀 1,330評論 0 4
  • 定位 //第一步導入框架 @import CoreLocation; //協(xié)議和屬性 @interface Loc...
    丶紳士丿丨丨閱讀 679評論 0 2