大家好,今天来为大家分享深入解析:iOS应用开发教程系列的一些知识点,和的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!
概述
如今,许多社交网络、电子商务、团购应用都引入了地图和定位功能。看来地图功能已经不再是地图应用和导航应用所独有的了。确实,拥有地图和定位功能确实让我们的生活变得更加丰富多彩,极大地改变了我们的生活方式。例如,如果您到达一个陌生的地方,想要查找附近的酒店、超市等,您可以打开软件搜索周边;同样,有很多团购软件可以根据你所在的位置自动向你推荐某些产品。总之,地图和定位功能已经被大量引入到应用开发中。今天我们就来看看iOS是如何开发地图和定位的。
1、定位
2. 地图
位置
要实现地图和导航功能,往往需要熟悉定位功能,通过iOS中的Core Location框架进行定位操作。 Core Location本身可以单独使用,完全独立于地图开发框架MapKit。但地图开发往往需要与定位框架结合使用。 Core Location主要包括定位和地理编码(包括反向编码)功能。
定位功能
定位是一个非常常用的功能。例如,如果用户在打开某些地图软件后允许软件定位,则打开软件后会自动锁定到当前位置。如果用户移动手机,当前位置也会发生变化。要实现该功能,需要使用Core Loaction中的CLLocationManager类。首先我们来看看这个类的一些主要方法和属性:
类方法说明
+ (BOOL)位置服务已启用;
是否开启定位服务。通常,如果用户没有开启定位服务,则可以提示用户开启定位服务。
+ (CLAuthorizationStatus)授权状态;
位置服务授权状态,返回枚举类型:
kCLAuthorizationStatusNotDetermined:用户尚未决定是否启用位置服务
kCLAuthorizationStatusRestricted:用户无权使用定位服务。用户自己可能没有禁止访问授权。
kCLAuthorizationStatusDenied:用户明确禁止应用程序使用定位服务或者当前系统定位服务已关闭。
kCLAuthorizationStatusAuthorizedAlways:应用程序被授权始终使用位置服务,即使应用程序未在使用中
kCLAuthorizationStatusAuthorizedWhenInUse:允许在使用此应用程序时访问位置服务
属性说明
期望准确度
定位精度,枚举类型:
kCLLocationAccuracyBest:最准确的定位
CLLocationAccuracy kCLLocationAccuracyNearestTenMeters:十米误差范围
kCLLocationAccuracyHundredMeters: 100 米误差范围
kCLLocationAccuracyKilometer:km 误差范围
kCLLocationAccuracy三公里:三千米误差范围
距离过滤器
位置信息更新的最小距离。仅当移动大于此距离时才会更新位置信息。默认值为kCLDistanceFilterNone:无距离限制。
对象方法说明
开始更新位置
开始定位跟踪。定位启动后,会按照用户设置的更新频率执行- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;反馈定位信息的方法
停止更新位置
停止位置跟踪
开始更新标题
开始导航方向跟踪
停止更新标题
停止导航方向跟踪
开始MonitoringForRegion:
开始定位并跟踪某个区域。开始定位一定区域后。当用户进入或离开某个区域时会被调用
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
和
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region代理方法反馈相关信息
停止MonitoringForRegion:
停止某个区域的位置跟踪
请求使用时授权
使用应用时请求获取定位服务授权。请注意,在使用此方法之前,必须在info.plist 中配置NSLocationWhenInUseUsageDescription。
请求始终授权
请求应用程序始终使用位置服务的权限。请注意,在使用此方法之前,必须在info.plist 中配置NSLocationAlwaysUsageDescription。
代理方法说明
-(void)locationManager:(CLLocationManager *)经理
didUpdateLocations:(NSArray *)位置;
位置变化后执行(也可以在第一次定位到某个位置后执行)
- (void)locationManager:(CLLocationManager *)经理
didUpdateHeading:(CLHeading *)newHeading;
导航方向改变后执行
- (void)locationManager:(CLLocationManager *)经理
didEnterRegion:(CLRegion *)区域;
进入某个区域后执行
- (void)locationManager:(CLLocationManager *)经理
didExitRegion:(CLRegion *)区域;
离开特定区域后执行
iOS 8还提供了更多人性化的位置服务选项。该应用程序的位置服务不再只是关闭或打开它。现在,启用位置服务有“从不”、“使用应用程序时”和“始终”三个选项。同时,考虑到能耗问题,如果某个应用程序需要在后台始终启用位置服务,iOS 8不仅会在您第一次打开该应用程序时主动询问您,还会弹出一个窗口提醒您该应用程序在日常使用过程中始终在后台运行。使用定位服务并征得您的许可才能继续。在iOS7及之前的版本中,如果在应用程序中使用位置服务,只要在程序中调用startUpdatingLocation方法,应用程序就会询问用户是否允许此应用程序允许使用位置服务。同时,在提示过程中,可以在info.plist中进行配置。通过配置隐私-位置使用描述告诉用户使用目的,该配置是可选的。
不过iOS8中配置项发生了变化。您可以通过配置NSLocationAlwaysUsageDescription 或NSLocationWhenInUseUsageDescription 告诉用户使用位置服务的目的。请注意,此配置是必要的。如果未配置,应用程序默认将无法使用位置服务。打开应用程序除非您在安装后自行设置该应用程序的定位服务,否则不会提示打开定位服务。同时需要根据配置在应用程序中请求requestAlwaysAuthorization或locationServicesEnabled方法。由于我的机器已经更新到最新的iOS8.1,以下内容主要针对iOS8,使用iOS7的朋友需要稍作调整。
////KCMainViewController.m//CoreLocation////由Kenshin Cui 于14-03-27 创建。//版权所有(c) 2014Kenshin Cui。版权所有。//#import"KCMainViewController.h"# import@interfaceKCMainViewController(){CLLocationManager*_locationManager;}@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad];//位置管理器_locationManager=[[CLLocationManageralloc]init] ;if(![CLLocationManagerlocationServicesEnabled]) {NSLog( @"当前可能未开启位置服务,请设置开启!");return; }//如果没有授权,请求用户授权if([CLLocationManagerauthorizationStatus]==kCLAuthorizationStatusNotDetermined){ [_locationManager requestWhenInUseAuthorization]; }elseif([CLLocationManagerauthorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse){//设置proxy_locationManager.delegate=self;//设置定位精度_locationManager.desiredAccuracy=kCLLocationAccuracyBest;//定位频率,每隔几米定位一次CLLocationDistancedistance=10.0;//每隔定位一次十米_locationManager .distanceFilter=distance;//开始跟踪定位[_locationManager startUpdatingLocation]; }}#pragma mark - CoreLocation代理#pragma mark跟踪定位代理方法,每次位置发生变化时都会执行该方法(只要定位到对应位置) //你可以通过模拟器设置一个虚拟位置,否则这个模拟器中无法调用该方法-(void)locationManager:(CLLocationManager*)manager didUpdateLocations:(NSArray*)locations{CLLocation*location=[locations firstObject];//获取第一个位置CLLocationCooperative2Dcoordinate=location.coordinate;//位置坐标NSLog (@"经度:%f,纬度:%f,海拔:%f,航向:%f,行走速度:%f",坐标.经度,坐标.纬度,位置.海拔,位置.路线,位置.速度) ;//如果不需要实时定位,使用后关闭定位服务[_locationManager stopUpdatingLocation];}@end
注意:
1、定位频率和定位精度并不是越准确越好。它们需要根据实际情况来确定,因为定位越准确,消耗的性能和功耗就越多。
2、定位成功后,会根据设置频繁调用-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations方法。该方法返回一组地理位置对象数组。每个元素都有一个代表地理位置信息(包括经度、纬度、海报、行走速度等信息)的CLLocation,之所以返回一个数组是因为有时一个位置点可能包含多个位置。
3、使用定位服务后,如果不需要实时监控,应立即关闭定位服务,以节省资源。
4、CLLocationManager除了提供定位功能外,还可以调用startMonitoringForRegion:方法对指定区域进行监控。
地理编码
除了提供位置跟踪功能外,位置服务还包含CLGeocoder 类,用于处理地理编码和反向地理编码(也称为反向地理编码)功能。
地理编码:确定给定位置(通常是地名)的地理坐标(纬度和经度)。
反向地理编码:可以根据地理坐标(经度、纬度)确定位置信息(街道、门牌号等)。
CLGeocoder 的两个主要方法是- (void)geocodeAddressString:(NSString *)addressStringcompletionHandler:(CLGeocodeCompletionHandler)completionHandler; - (void)reverseGeocodeLocation:(CLLocation *)locationcompletionHandler:(CLGeocodeCompletionHandler)completionHandler;分别用于地理编码和反向地理。编码。这是一个简单的演示:
////KCMainViewController.m//CoreLocation////由Kenshin Cui 于14-03-27 创建。//版权所有(c) 2014Kenshin Cui。保留所有权利。//#import"KCMainViewController.h"# import@interfaceKCMainViewController(){CLGeocoder*_geocoder;}@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; } _geocoder=[[CLGeocoderalloc]init]; [selfgetCooperativeByAddress:@"北京"]; [selfgetAddressByLatitude:39.54long itude:116.28]; }#pragma mark 根据地名确定地理坐标-(void)getCooperativeByAddress:(NSString*)address{//Geocode[_geocoder geocodeAddressString:addresscompletionHandler:^(NSArray*placemarks,NSError*error) {//获取第一个地标并存储在地标详细地址信息,注:一个地名可能搜索到多个地址CLPlacemark*placemark=[placemarks firstObject];CLLocation*location=placemark.location;//位置CLRegion*region=placemark.region;//区域NSDictionary*addressDic=placemark.addressDictionary;//详细地址信息字典,包括以下部分信息//NSString *name=placemark.name;//地名//NSString *thoroughfare=placemark.thoroughfare;//街道//NSString *subThoroughfare=地标。次干道; //街道相关信息,比如门牌号等//NSString *locality=placemark.locality; //城市//NSString *subLocality=placemark.subLocality; //城市相关信息,如地标建筑//NSString *administrativeArea=placemark.administrativeArea; //状态//NSString *subAdministrativeArea=placemark.subAdministrativeArea; //其他行政区域信息//NSString *postalCode=placemark.postalCode; //邮政编码//NSString *ISOcountryCode=placemark.ISOcountryCode; //国家/地区代码//NSString *country=placemark.country; //国家//NSString *inlandWater=placemark.inlandWater; //水源、湖泊//NSString *ocean=placemark.ocean; //Ocean//NSArray *areasOfInterest=placemark.areasOfInterest ; //关联或兴趣相关的地标NSLog(@"location:%@,region:%@,详细信息:%@",location,region,addressDic); }];}#pragma mark 根据坐标获取地名- (void)getAddressByLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude{//反向地理编码CLLocation*location=[[CLLocationalloc]initWithLatitude:latitude longitude:longitude]; [_geocoderverseGeocodeLocation:locationcompletionHandler:^(NSArray*placemarks, NSError*error) {CLPlacemark*placemark=[placemarksfirstObject];NSLog(@"详细信息:%@",placemark.addressDictionary); }];}@结尾
地图
从iOS 6.0开始,地图数据不再由Google驱动,而是使用自己的地图。当然,它在中国的数据是由高德地图提供的。这样,如果你在iOS 6.0之前开发地图,使用方法就会有所不同。就目前的情况来看,使用iOS 6.0之前版本的系统基本上很少。所有这些都不会在下面的内容中讨论。 iOS5及之前版本的地图开发介绍。
在iOS中开发地图主要有两种方法。一是直接使用MapKit框架进行地图开发,可以精准控制地图;另一种是直接调用苹果官方地图应用。主要用于一些简单的地图应用(例如:填充导航叠加层等),无法提供精确的控制。当然,本节的重点仍然是前者,后面的内容还会有一些提示。
在使用MapKit之前,需要简单了解MapKit中地图显示控件MKMapView的一些常用属性和方法,具体如下表:
属性说明
用户追踪模式
跟踪类型是一个枚举:
MKUserTrackingModeNone : 不执行用户位置跟踪;
MKUserTrackingModeFollow : 跟踪用户位置;
MKUserTrackingModeFollowWithHeading : 跟踪用户的位置并跟踪用户的方向;
地图类型
地图类型是枚举:
MKMapTypeStandard :标准地图,一般这个地图就足够了;
MKMapTypeSatellite:卫星地图;
MKMapTypeHybrid:混合地图,加载最慢且消耗资源;
用户位置
用户位置,只读属性
注释
当前地图中的所有引脚,只读属性
对象方法说明
- (void)addAnnotation:(id )注释;
添加一个引脚,对应添加一个引脚数组
- (void)removeAnnotation:(id )注释;
删除引脚,对应删除引脚数组
- (void)setRegion:(MKCooperativeRegion)区域动画:(BOOL)动画;
设置地图显示区域,用于控制当前屏幕显示地图范围
- (void)setCenterCooperative:(CLLocationCooperative2D)坐标动画:(BOOL)动画;
设置地图中心点位置
- (CGPoint)convertCooperative:(CLLocationCooperative2D)坐标为PointToView:(UIView *)视图;
将地理坐标(纬度和经度)转换为数学坐标(UIKit 坐标)
- (CLLocationCooperative2D)convertPoint:(CGPoint)指向CoordinateFromView:(UIView *)视图;
将数学坐标转换为地理坐标
- (MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)标识符;
从缓存池中检索pin 与从UITableView 中检索UITableViewCell 类似,旨在优化性能。
- (void)selectAnnotation:(id )注释动画:(BOOL)动画;
选择指定引脚
- (void)取消选择Annotation:(id)注释动画:(BOOL)动画;
取消选中指定的引脚
代理方法说明
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
当用户位置发生变化时触发(首次定位用户位置时也会触发该方法)
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
显示区域变化后触发
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;
地图加载后触发
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )注释;
当显示引脚并返回引脚视图时触发。通常定制引脚可以通过这种方法定制。
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
单击图钉以选择时触发
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
取消选择引脚时触发
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay
渲染地图叠加层时触发
用户位置追踪
在很多带有地图的应用中,默认打开地图时,会显示用户当前所在位置,并且当前位置会被标记并放置在屏幕中间。
方便用户对周围情况进行查看。如果在iOS6或者iOS7中实现这个功能只需要添加地图控件、设置用户跟踪模式、在-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation代理方法中设置地图中心区域及显示范围。但是在iOS8中用法稍有不同: 1.由于在地图中进行用户位置跟踪需要使用定位功能,而定位功能在iOS8中设计发生了变化,因此必须按照前面定位章节中提到的内容进行配置和请求。 2.iOS8中不需要进行中心点的指定,默认会将当前位置设置中心点并自动设置显示区域范围。 了解以上两点,要进行用户位置跟踪其实就相当简单了,值得一提的是-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation这个代理方法。这个方法只有在定位(利用前面章节中的定位内容)到当前位置之后就会调用,以后每当用户位置发生改变就会触发,调用频率相当频繁。 大头针 在iOS开发中经常会标记某个位置,需要使用地图标注,也就是大家俗称的“大头针”。只要一个NSObject类实现MKAnnotation协议就可以作为一个大头针,通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性,然后在程序中创建大头针对象并调用addAnnotation:方法添加大头针即可(之所以iOS没有定义一个基类实现这个协议供开发者使用,多数原因应该是MKAnnotation是一个模型对象,对于多数应用模型会稍有不同,例如后面的内容中会给大头针模型对象添加其他属性)。 KCAnnotation.h //// KCAnnotation.h// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import#import@interfaceKCAnnotation:NSObject@property(nonatomic)CLLocationCoordinate2Dcoordinate;@property(nonatomic,copy)NSString*title;@property(nonatomic,copy)NSString*subtitle;@end KCMainViewController.m //// KCMainViewController.m// MapKit Annotation//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.// 37.785834 -122.406417// 39.92 116.39#import"KCMainViewController.h"#import#import#import"KCAnnotation.h"@interfaceKCMainViewController(){CLLocationManager*_locationManager;MKMapView*_mapView;}@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; [selfinitGUI];}#pragma mark 添加地图控件-(void)initGUI{CGRectrect=[UIScreenmainScreen].bounds; _mapView=[[MKMapViewalloc]initWithFrame:rect]; [self.view addSubview:_mapView];//设置代理_mapView.delegate=self;//请求定位服务_locationManager=[[CLLocationManageralloc]init];if(![CLLocationManagerlocationServicesEnabled]||[CLLocationManagerauthorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){ [_locationManager requestWhenInUseAuthorization]; }//用户位置追踪(用户位置追踪用于标记用户当前位置,此时会调用定位服务)_mapView.userTrackingMode=MKUserTrackingModeFollow;//设置地图类型_mapView.mapType=MKMapTypeStandard;//添加大头针[selfaddAnnotation];}#pragma mark 添加大头针-(void)addAnnotation{CLLocationCoordinate2Dlocation1=CLLocationCoordinate2DMake(39.95,116.35); KCAnnotation *annotation1=[[KCAnnotation alloc]init]; annotation1.title=@"CMJ Studio"; annotation1.subtitle=@"Kenshin Cui"s Studios"; annotation1.coordinate=location1; [_mapView addAnnotation:annotation1];CLLocationCoordinate2Dlocation2=CLLocationCoordinate2DMake(39.87,116.35); KCAnnotation *annotation2=[[KCAnnotation alloc]init]; annotation2.title=@"Kenshin&Kaoru"; annotation2.subtitle=@"Kenshin Cui"s Home"; annotation2.coordinate=location2; [_mapView addAnnotation:annotation2];}#pragma mark - 地图控件代理方法#pragma mark 更新用户位置,只要用户改变则调用此方法(包括第一次定位到用户位置)-(void)mapView:(MKMapView*)mapView didUpdateUserLocation:(MKUserLocation*)userLocation{NSLog(@"%@",userLocation);//设置地图显示范围(如果不进行区域设置会自动显示区域范围并指定当前用户位置为地图中心点)// MKCoordinateSpan span=MKCoordinateSpanMake(0.01, 0.01);// MKCoordinateRegion region=MKCoordinateRegionMake(userLocation.location.coordinate, span);// [_mapView setRegion:region animated:true];}@end 运行效果: 设置大头针视图 在一些应用中系统默认的大头针样式可能无法满足实际的需求,此时就需要修改大头针视图默认样式。根据前面MapKit的代理方法不难发现- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation;方法可以返回一个大头针视图,只要实现这个方法并在这个方法中定义一个大头针视图MKAnnotationView对象并设置相关属性就可以改变默认大头针的样式。MKAnnotationView常用属性: 属性说明 annotation 大头针模型信息,包括标题、子标题、地理位置。 image 大头针图片 canShowCallout 点击大头针是否显示标题、子标题内容等,注意如果在 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation; 方法中重新定义大头针默认情况是无法交互的需要设置为true。 calloutOffset 点击大头针时弹出详情信息视图的偏移量 selected 是否被选中状态 leftCalloutAccessoryView 弹出详情左侧视图 rightCalloutAccessoryView 弹出详情右侧视图 需要注意: a.这个代理方法的调用时机:每当有大头针显示到系统可视界面中时就会调用此方法返回一个大头针视图放到界面中,同时当前系统位置标注(也就是地图中蓝色的位置点)也是一个大头针,也会调用此方法,因此处理大头针视图时需要区别对待。 b.类似于UITableView的代理方法,此方法调用频繁,开发过程中需要重复利用MapKit的缓存池将大头针视图缓存起来重复利用。 c.自定义大头针默认情况下不允许交互,如果交互需要设置canShowCallout=true d.如果代理方法返回nil则会使用默认大头针视图,需要根据情况设置。 下面以一个示例进行大头针视图设置,这里设置了大头针的图片、弹出视图、偏移量等信息。 KCAnnotation.h //// KCAnnotation.h// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import#import@interfaceKCAnnotation:NSObject@property(nonatomic)CLLocationCoordinate2Dcoordinate;@property(nonatomic,copy)NSString*title;@property(nonatomic,copy)NSString*subtitle;#pragma mark 自定义一个图片属性在创建大头针视图时使用@property(nonatomic,strong)UIImage*image;@end KCMainViewController.m //// KCMainViewController.m// MapKit Annotation//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.// 37.785834 -122.406417// 39.92 116.39#import"KCMainViewController.h"#import#import#import"KCAnnotation.h"@interfaceKCMainViewController(){CLLocationManager*_locationManager;MKMapView*_mapView;}@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; [selfinitGUI];}#pragma mark 添加地图控件-(void)initGUI{CGRectrect=[UIScreenmainScreen].bounds; _mapView=[[MKMapViewalloc]initWithFrame:rect]; [self.view addSubview:_mapView];//设置代理_mapView.delegate=self;//请求定位服务_locationManager=[[CLLocationManageralloc]init];if(![CLLocationManagerlocationServicesEnabled]||[CLLocationManagerauthorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){ [_locationManager requestWhenInUseAuthorization]; }//用户位置追踪(用户位置追踪用于标记用户当前位置,此时会调用定位服务)_mapView.userTrackingMode=MKUserTrackingModeFollow;//设置地图类型_mapView.mapType=MKMapTypeStandard;//添加大头针[selfaddAnnotation];}#pragma mark 添加大头针-(void)addAnnotation{CLLocationCoordinate2Dlocation1=CLLocationCoordinate2DMake(39.95,116.35); KCAnnotation *annotation1=[[KCAnnotation alloc]init]; annotation1.title=@"CMJ Studio"; annotation1.subtitle=@"Kenshin Cui"s Studios"; annotation1.coordinate=location1; annotation1.image=[UIImageimageNamed:@"icon_pin_floating.png"]; [_mapView addAnnotation:annotation1];CLLocationCoordinate2Dlocation2=CLLocationCoordinate2DMake(39.87,116.35); KCAnnotation *annotation2=[[KCAnnotation alloc]init]; annotation2.title=@"Kenshin&Kaoru"; annotation2.subtitle=@"Kenshin Cui"s Home"; annotation2.coordinate=location2; annotation2.image=[UIImageimageNamed:@"icon_paopao_waterdrop_streetscape.png"]; [_mapView addAnnotation:annotation2];}#pragma mark - 地图控件代理方法#pragma mark 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象-(MKAnnotationView*)mapView:(MKMapView*)mapView viewForAnnotation:(id)annotation{//由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图if([annotation isKindOfClass:[KCAnnotationclass]]) {staticNSString*key1=@"AnnotationKey1";MKAnnotationView*annotationView=[_mapView dequeueReusableAnnotationViewWithIdentifier:key1];//如果缓存池中不存在则新建if(!annotationView) { annotationView=[[MKAnnotationViewalloc]initWithAnnotation:annotation reuseIdentifier:key1]; annotationView.canShowCallout=true;//允许交互点击annotationView.calloutOffset=CGPointMake(0,1);//定义详情视图偏移量annotationView.leftCalloutAccessoryView=[[UIImageViewalloc]initWithImage:[UIImageimageNamed:@"icon_classify_cafe.png"]];//定义详情左侧视图}//修改大头针视图//重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)annotationView.annotation=annotation; annotationView.image=((KCAnnotation *)annotation).image;//设置大头针视图的图片returnannotationView; }else{returnnil; }}@end 运行效果: 注意: 在MapKit框架中除了MKAnnotationView之外还有一个MKPinAnnotationView,它是MKAnnotationView的子类,相比MKAnnotationView多了两个属性pinColor和animationDrop,分别用于设置大头针视图颜色和添加大头针动画。 扩展--自定义大头针弹详情视图 通过上面的示例不难看出MKAnnotationView足够强大(何况还有MKPinAnnotationView),很多信息都可以进行设置,但是唯独不能修改大头针描述详情视图(仅仅支持详情中左右视图内容)。要实现这个需求目前开发中普遍采用的思路就是: a.点击一个大头针A时重新在A的坐标处添加另一个大头针B(注意此时将A对应的大头针视图canShowCallout设置为false)作为大头针详情模型,然后在- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation;代理方法中判断大头针类型,如果是B则重写MKAnnotationView(可以自定义一个类C继承于MKAnnotationView),返回自定义大头针视图C。 b.定义大头针视图C继承于MKAnnotationView(或者MKPinAnnotationView),在自定义大头针视图中添加自己的控件,完成自定义布局。 在使用百度地图客户端时当点击一个搜索位置时可以看到此位置的评价等信息,视图效果大概如下: 下面不妨试着实现一下这个效果: 大头针模型:KCAnnotation.h //// KCAnnotation.h// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import#import@interfaceKCAnnotation:NSObject@property(nonatomic)CLLocationCoordinate2Dcoordinate;@property(nonatomic,copy)NSString*title;@property(nonatomic,copy)NSString*subtitle;#pragma mark 自定义一个图片属性在创建大头针视图时使用@property(nonatomic,strong)UIImage*image;#pragma mark 大头针详情左侧图标@property(nonatomic,strong)UIImage*icon;#pragma mark 大头针详情描述@property(nonatomic,copy)NSString*detail;#pragma mark 大头针右下方星级评价@property(nonatomic,strong)UIImage*rate;@end 弹出详情大头针模型:KCCalloutAnnotation.h //// KCCalloutAnnotation.h// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import#import#import@interfaceKCCalloutAnnotation:NSObject@property(nonatomic)CLLocationCoordinate2Dcoordinate;@property(nonatomic,copy,readonly)NSString*title;@property(nonatomic,copy,readonly)NSString*subtitle;#pragma mark 左侧图标@property(nonatomic,strong)UIImage*icon;#pragma mark 详情描述@property(nonatomic,copy)NSString*detail;#pragma mark 星级评价@property(nonatomic,strong)UIImage*rate;@end 弹出详情大头针视图:KCCalloutAnnotatonView.h //// KCCalloutView.h// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.// 自定义弹出标注视图#import#import#import#import"KCCalloutAnnotation.h"@interfaceKCCalloutAnnotationView:MKAnnotationView@property(nonatomic,strong) KCCalloutAnnotation *annotation;#pragma mark 从缓存取出标注视图+(instancetype)calloutViewWithMapView:(MKMapView*)mapView;@end KCCalloutAnnotationView.m //// KCCalloutView.m// MapKit//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import"KCCalloutAnnotationView.h"#define kSpacing 5#define kDetailFontSize 12#define kViewOffset 80@interfaceKCCalloutAnnotationView(){UIView*_backgroundView;UIImageView*_iconView;UILabel*_detailLabel;UIImageView*_rateView;}@end@implementationKCCalloutAnnotationView-(instancetype)init{if(self=[superinit]){ [selflayoutUI]; }returnself;}-(instancetype)initWithFrame:(CGRect)frame{if(self=[superinitWithFrame:frame]) { [selflayoutUI]; }returnself;}-(void)layoutUI{//背景_backgroundView=[[UIViewalloc]init]; _backgroundView.backgroundColor=[UIColorwhiteColor];//左侧添加图标_iconView=[[UIImageViewalloc]init];//上方详情_detailLabel=[[UILabelalloc]init]; _detailLabel.lineBreakMode=NSLineBreakByWordWrapping;//[_text sizeToFit];_detailLabel.font=[UIFontsystemFontOfSize:kDetailFontSize];//下方星级_rateView=[[UIImageViewalloc]init]; [selfaddSubview:_backgroundView]; [selfaddSubview:_iconView]; [selfaddSubview:_detailLabel]; [selfaddSubview:_rateView];}+(instancetype)calloutViewWithMapView:(MKMapView*)mapView{staticNSString*calloutKey=@"calloutKey1"; KCCalloutAnnotationView *calloutView=(KCCalloutAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:calloutKey];if(!calloutView) { calloutView=[[KCCalloutAnnotationView alloc]init]; }returncalloutView;}#pragma mark 当给大头针视图设置大头针模型时可以在此处根据模型设置视图内容-(void)setAnnotation:(KCCalloutAnnotation *)annotation{ [supersetAnnotation:annotation];//根据模型调整布局_iconView.image=annotation.icon; _iconView.frame=CGRectMake(kSpacing, kSpacing, annotation.icon.size.width, annotation.icon.size.height); _detailLabel.text=annotation.detail;floatdetailWidth=150.0;CGSizedetailSize= [annotation.detail boundingRectWithSize:CGSizeMake(detailWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOriginattributes:@{NSFontAttributeName: [UIFontsystemFontOfSize:kDetailFontSize]} context:nil].size;floatdetailX=CGRectGetMaxX(_iconView.frame)+kSpacing; _detailLabel.frame=CGRectMake(detailX, kSpacing, detailSize.width, detailSize.height); _rateView.image=annotation.rate; _rateView.frame=CGRectMake(detailX,CGRectGetMaxY(_detailLabel.frame)+kSpacing, annotation.rate.size.width, annotation.rate.size.height);floatbackgroundWidth=CGRectGetMaxX(_detailLabel.frame)+kSpacing;floatbackgroundHeight=_iconView.frame.size.height+2*kSpacing; _backgroundView.frame=CGRectMake(0,0, backgroundWidth, backgroundHeight);self.bounds=CGRectMake(0,0, backgroundWidth, backgroundHeight+kViewOffset); }@end 主视图控制器:KCMainViewController.m //// KCMainViewController.m// MapKit Annotation//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.// 37.785834 -122.406417// 39.92 116.39#import"KCMainViewController.h"#import#import#import"KCAnnotation.h"#import"KCCalloutAnnotationView.h"#import"KCCalloutAnnotationView.h"@interfaceKCMainViewController(){CLLocationManager*_locationManager;MKMapView*_mapView;}@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; [selfinitGUI];}#pragma mark 添加地图控件-(void)initGUI{CGRectrect=[UIScreenmainScreen].bounds; _mapView=[[MKMapViewalloc]initWithFrame:rect]; [self.view addSubview:_mapView];//设置代理_mapView.delegate=self;//请求定位服务_locationManager=[[CLLocationManageralloc]init];if(![CLLocationManagerlocationServicesEnabled]||[CLLocationManagerauthorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){ [_locationManager requestWhenInUseAuthorization]; }//用户位置追踪(用户位置追踪用于标记用户当前位置,此时会调用定位服务)_mapView.userTrackingMode=MKUserTrackingModeFollow;//设置地图类型_mapView.mapType=MKMapTypeStandard;//添加大头针[selfaddAnnotation];}#pragma mark 添加大头针-(void)addAnnotation{CLLocationCoordinate2Dlocation1=CLLocationCoordinate2DMake(39.95,116.35); KCAnnotation *annotation1=[[KCAnnotation alloc]init]; annotation1.title=@"CMJ Studio"; annotation1.subtitle=@"Kenshin Cui"s Studios"; annotation1.coordinate=location1; annotation1.image=[UIImageimageNamed:@"icon_pin_floating.png"]; annotation1.icon=[UIImageimageNamed:@"icon_mark1.png"]; annotation1.detail=@"CMJ Studio..."; annotation1.rate=[UIImageimageNamed:@"icon_Movie_Star_rating.png"]; [_mapView addAnnotation:annotation1];CLLocationCoordinate2Dlocation2=CLLocationCoordinate2DMake(39.87,116.35); KCAnnotation *annotation2=[[KCAnnotation alloc]init]; annotation2.title=@"Kenshin&Kaoru"; annotation2.subtitle=@"Kenshin Cui"s Home"; annotation2.coordinate=location2; annotation2.image=[UIImageimageNamed:@"icon_paopao_waterdrop_streetscape.png"]; annotation2.icon=[UIImageimageNamed:@"icon_mark2.png"]; annotation2.detail=@"Kenshin Cui..."; annotation2.rate=[UIImageimageNamed:@"icon_Movie_Star_rating.png"]; [_mapView addAnnotation:annotation2];}#pragma mark - 地图控件代理方法#pragma mark 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象-(MKAnnotationView*)mapView:(MKMapView*)mapView viewForAnnotation:(id)annotation{//由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图if([annotation isKindOfClass:[KCAnnotationclass]]) {staticNSString*key1=@"AnnotationKey1";MKAnnotationView*annotationView=[_mapView dequeueReusableAnnotationViewWithIdentifier:key1];//如果缓存池中不存在则新建if(!annotationView) { annotationView=[[MKAnnotationViewalloc]initWithAnnotation:annotation reuseIdentifier:key1];// annotationView.canShowCallout=true;//允许交互点击annotationView.calloutOffset=CGPointMake(0,1);//定义详情视图偏移量annotationView.leftCalloutAccessoryView=[[UIImageViewalloc]initWithImage:[UIImageimageNamed:@"icon_classify_cafe.png"]];//定义详情左侧视图}//修改大头针视图//重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)annotationView.annotation=annotation; annotationView.image=((KCAnnotation *)annotation).image;//设置大头针视图的图片returnannotationView; }elseif([annotation isKindOfClass:[KCCalloutAnnotationclass]]){//对于作为弹出详情视图的自定义大头针视图无弹出交互功能(canShowCallout=false,这是默认值),在其中可以自由添加其他视图(因为它本身继承于UIView)KCCalloutAnnotationView *calloutView=[KCCalloutAnnotationView calloutViewWithMapView:mapView]; calloutView.annotation=annotation;returncalloutView; }else{returnnil; }}#pragma mark 选中大头针时触发//点击一般的大头针KCAnnotation时添加一个大头针作为所点大头针的弹出详情视图-(void)mapView:(MKMapView*)mapView didSelectAnnotationView:(MKAnnotationView*)view{ KCAnnotation *annotation=view.annotation;if([view.annotation isKindOfClass:[KCAnnotationclass]]) {//点击一个大头针时移除其他弹出详情视图// [self removeCustomAnnotation];//添加详情大头针,渲染此大头针视图时将此模型对象赋值给自定义大头针视图完成自动布局KCCalloutAnnotation *annotation1=[[KCCalloutAnnotation alloc]init]; annotation1.icon=annotation.icon; annotation1.detail=annotation.detail; annotation1.rate=annotation.rate; annotation1.coordinate=view.annotation.coordinate; [mapView addAnnotation:annotation1]; }}#pragma mark 取消选中时触发-(void)mapView:(MKMapView*)mapView didDeselectAnnotationView:(MKAnnotationView*)view{ [selfremoveCustomAnnotation];}#pragma mark 移除所用自定义大头针-(void)removeCustomAnnotation{ [_mapView.annotations enumerateObjectsUsingBlock:^(idobj,NSUIntegeridx,BOOL*stop) {if([obj isKindOfClass:[KCCalloutAnnotationclass]]) { [_mapView removeAnnotation:obj]; } }];}@end 在这个过程中需要注意几点: 1.大头针A作为一个普通大头针,其中最好保存自定义大头针视图C所需要的模型以便根据不同的模型初始化视图。 2.自定义大头针视图C的大头针模型B中不需要title、subtitle属性,最好设置为只读;模型中最后保存自定义大头针视图C所需要的布局模型数据。 3.只有点击非B类大头针时才新增自定义大头针,并且增加时要首先移除其他B类大头针避免重叠(一般建议放到取消大头针选择的代理方法中)。 4.通常在自定义大头针视图C设置大头针模型时布局界面,此时需要注意新增大头针的位置,通常需要偏移一定的距离才能达到理想的效果。 运行效果: 使用自带的地图应用 除了可以使用MapKit框架进行地图开发,对地图有精确的控制和自定义之外,如果对于应用没有特殊要求的话选用苹果自带的地图应用也是一个不错的选择。使用苹果自带的应用时需要用到MapKit中的MKMapItem类,这个类有一个openInMapsWithLaunchOptions:动态方法和一个openMapsWithItems: launchOptions:静态方法用于打开苹果地图应用。第一个方法用于在地图上标注一个位置,第二个方法除了可以标注多个位置外还可以进行多个位置之间的驾驶导航,使用起来也是相当方便。在熟悉这两个方法使用之前有必要对两个方法中的options参数做一下简单说明: 键(常量)说明值 MKLaunchOptionsDirectionsModeKey 路线模式,常量 MKLaunchOptionsDirectionsModeDriving 驾车模式 MKLaunchOptionsDirectionsModeWalking 步行模式 MKLaunchOptionsMapTypeKey 地图类型,枚举 MKMapTypeStandard :标准模式 MKMapTypeSatellite :卫星模式 MKMapTypeHybrid :混合模式 MKLaunchOptionsMapCenterKey 中心点坐标,CLLocationCoordinate2D类型 MKLaunchOptionsMapSpanKey 地图显示跨度,MKCoordinateSpan 类型 MKLaunchOptionsShowsTrafficKey 是否 显示交通状况,布尔型 MKLaunchOptionsCameraKey 3D地图效果,MKMapCamera类型 注意:此属性从iOS7及以后可用,前面的属性从iOS6开始可用 单个位置的标注 下面的代码演示了如何在苹果自带地图应用上标记一个位置,首先根据反地理编码获得一个CLPlacemark位置对象,然后将其转换为MKPlacemark对象用于MKMapItem初始化,最后调用其openInMapsWithLaunchOptions:打开地图应用并标记: //// KCMainViewController.m// AppleMap//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import"KCMainViewController.h"#import#import@interfaceKCMainViewController()@property(nonatomic,strong)CLGeocoder*geocoder;@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; _geocoder=[[CLGeocoderalloc]init]; [selflocation];}#pragma mark 在地图上定位-(void)location{//根据“北京市”进行地理编码[_geocoder geocodeAddressString:@"北京市"completionHandler:^(NSArray*placemarks,NSError*error) {CLPlacemark*clPlacemark=[placemarks firstObject];//获取第一个地标MKPlacemark*mkplacemark=[[MKPlacemarkalloc]initWithPlacemark:clPlacemark];//定位地标转化为地图的地标NSDictionary*options=@{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard)};MKMapItem*mapItem=[[MKMapItemalloc]initWithPlacemark:mkplacemark]; [mapItem openInMapsWithLaunchOptions:options]; }];}@end 运行效果: 标记多个位置 如果要标记多个位置需要调用MKMapItem的静态方法,下面的代码演示中需要注意,使用CLGeocoder进行定位时一次只能定位到一个位置,所以第二个位置定位放到了第一个位置获取成功之后。 //// KCMainViewController.m// AppleMap//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import"KCMainViewController.h"#import#import@interfaceKCMainViewController()@property(nonatomic,strong)CLGeocoder*geocoder;@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; _geocoder=[[CLGeocoderalloc]init]; [selflistPlacemark];}-(void)listPlacemark{//根据“北京市”进行地理编码[_geocoder geocodeAddressString:@"北京市"completionHandler:^(NSArray*placemarks,NSError*error) {CLPlacemark*clPlacemark1=[placemarks firstObject];//获取第一个地标MKPlacemark*mkPlacemark1=[[MKPlacemarkalloc]initWithPlacemark:clPlacemark1];//注意地理编码一次只能定位到一个位置,不能同时定位,所在放到第一个位置定位完成回调函数中再次定位[_geocoder geocodeAddressString:@"郑州市"completionHandler:^(NSArray*placemarks,NSError*error) {CLPlacemark*clPlacemark2=[placemarks firstObject];//获取第一个地标MKPlacemark*mkPlacemark2=[[MKPlacemarkalloc]initWithPlacemark:clPlacemark2];NSDictionary*options=@{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard)};//MKMapItem *mapItem1=[MKMapItem mapItemForCurrentLocation];//当前位置MKMapItem*mapItem1=[[MKMapItemalloc]initWithPlacemark:mkPlacemark1];MKMapItem*mapItem2=[[MKMapItemalloc]initWithPlacemark:mkPlacemark2]; [MKMapItemopenMapsWithItems:@[mapItem1,mapItem2] launchOptions:options]; }]; }];}@end 运行效果: 地图导航 要使用地图导航功能在自带地图应用中相当简单,只要设置参数配置导航模式即可,例如在上面代码基础上设置驾驶模式,则地图应用会启动驾驶模式计算两点之间的距离同时对路线进行规划。 //// KCMainViewController.m// AppleMap//// Created by Kenshin Cui on 14/3/27.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import"KCMainViewController.h"#import#import@interfaceKCMainViewController()@property(nonatomic,strong)CLGeocoder*geocoder;@end@implementationKCMainViewController- (void)viewDidLoad { [superviewDidLoad]; _geocoder=[[CLGeocoderalloc]init]; [selfturnByTurn];}-(void)turnByTurn{//根据“北京市”地理编码[_geocoder geocodeAddressString:@"北京市"completionHandler:^(NSArray*placemarks,NSError*error) {CLPlacemark*clPlacemark1=[placemarks firstObject];//获取第一个地标MKPlacemark*mkPlacemark1=[[MKPlacemarkalloc]initWithPlacemark:clPlacemark1];//注意地理编码一次只能定位到一个位置,不能同时定位,所在放到第一个位置定位完成回调函数中再次定位[_geocoder geocodeAddressString:@"郑州市"completionHandler:^(NSArray*placemarks,NSError*error) {CLPlacemark*clPlacemark2=[placemarks firstObject];//获取第一个地标MKPlacemark*mkPlacemark2=[[MKPlacemarkalloc]initWithPlacemark:clPlacemark2];NSDictionary*options=@{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard),MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};//MKMapItem *mapItem1=[MKMapItem mapItemForCurrentLocation];//当前位置MKMapItem*mapItem1=[[MKMapItemalloc]initWithPlacemark:mkPlacemark1];MKMapItem*mapItem2=[[MKMapItemalloc]initWithPlacemark:mkPlacemark2]; [MKMapItemopenMapsWithItems:@[mapItem1,mapItem2] launchOptions:options]; }]; }];}@end 运行效果: 注意:其实如果不用苹果自带的地图应用也可以实现地图导航,MapKit中提供了MKDirectionRequest对象用于计算路线,提供了MKDirections用于计算方向,这样一来只需要调用MKMapView的addOverlay等方法添加覆盖物即可实现类似的效果,有兴趣的朋友可以试一下。 由于定位和地图框架中用到了诸多类,有些初学者容易混淆,下面简单对比一下。 CLLocation:用于表示位置信息,包含地理坐标、海拔等信息,包含在CoreLoaction框架中。 MKUserLocation:一个特殊的大头针,表示用户当前位置。【深入解析:iOS应用开发教程系列】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
打算学习iOS开发,这篇文章正好可以看看入门知识。
有7位网友表示赞同!
终于有人写了针对iOS开发的教程!期待后续文章内容丰富。
有9位网友表示赞同!
我想了解一下iOS开发的基本框架是什么样的。
有20位网友表示赞同!
很久没碰iOS开发了,这系列文章帮到我了。
有18位网友表示赞同!
这篇标题很有吸引力!希望内容能详细讲解每个模块。
有20位网友表示赞同!
最近想要试试手机游戏的开发,不知道iOS平台有哪些注意事项?
有20位网友表示赞同!
从基础开始学习iOS开发,这样的学习路线比较适合新手。
有12位网友表示赞同!
想看一些iOS项目案例分析,希望能学到更多实践经验。
有15位网友表示赞同!
对编程语言Swift和Objective-C有一些了解,想知道这两者在iOS开发中的应用。
有19位网友表示赞同!
需要一些帮助来理解iOS UI设计规范,这篇文章能帮得着我吗?
有11位网友表示赞同!
iOS开发的调试技巧我一直不太熟练,希望在这系列文章中能有所提升。
有5位网友表示赞同!
学习iOS开发的一个好的起点!后续文章记得更新哦。
有7位网友表示赞同!
感觉 iOS 开发是一个很有机会的发展方向! 期待了解更多知识。
有18位网友表示赞同!
想看看 iOS 开发的就业前景怎么样? 文章里有没有提到这些方面呢?
有17位网友表示赞同!
这篇文章的内容应该涵盖各个阶段的开发流程吧?
有13位网友表示赞同!
我有一个App的想法,想要用iOS平台实现它。 这系列文章能给我一些指导吗?
有15位网友表示赞同!
期待看一看 iOS 开发社区的一些资源和建议
有6位网友表示赞同!
学习iOS开发是一个循序渐进的过程,这篇文章能帮助我更好地完成每个阶段的任务。
有6位网友表示赞同!
我的编程基础还很薄弱,希望这篇文章能够从入门级别讲解。
有9位网友表示赞同!
希望能看到一些实战案例和代码示例,加深理解。
有6位网友表示赞同!