iOS GPS 定位应用 iOS 多线程 iOS 手势处理 iOS GPS 定位应用 本章内容概述 为项目添加必要框架 使用MKMapView显示地图 应用MKMapView的代理MKMapViewDelegate 使用CLLocationManager获得设备当前经纬度信息 在地图上标注位置 使用CLGeocoder将位置描述转换为经纬度 使用CLGeocoder将经纬度转换为位置描述 使用Google Place API 查询周边位置信息 在iOS SDK中提供了两个框架,来实现位置服务。这两个框架分别是:CoreLocaton.framework和MapKit.framework,CoreLocation.framework主要提供了获得设备位置信息的API,例如,经纬度信息,而MapKit.framework主要提供了展示地图的API。这两个框架中的核心类是CLLocationManager类和MKMapView类,CLLocationManager提供了获得位置信息的功能,MKMapView提供了展示地图的功能。我们可以使用CLGeoCoder类来实现位置描述和经纬度之间的转换,可以通过一些其他API来实现位置标注、距离测量等功能。另外,我们还可以通过Google Place API查询周边位置信息。 1.1 为项目添加必要的框架 使用XCode模板创建的项目,默认情况下是没有添加CoreLocation.framewok和MapKit.framework框架的,所以在使用之前必须添加这两个框架,并导入必要的头文件。 实现步骤如下: 选择项目名称。 在右边选择TARGETS。 在右边选择Build Phases。 在下面的Link Binary With Libraries中点击"+"找到这两个框架并添加之。 在头文件中包含这两个框架的头文件,代码如下: #import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> @interface AmakerViewController : UIViewController @end 添加过程如下图所示: 1.2 使用MKMapView显示地图 iOS中显示地图非常简单,最简单的做法是在界面中添加MapView组件运行项目即可显示地图,但是,大部分情况下我们还是需要自己定义一些功能来显示地图的,例如,改变地图的显示类型,可以显示标准、卫星和混合等不同类型的地图。 1.2.1 使用MapView组件直接显示地图 使用MapView显示地图非常简单只要将MapView组件放在界面上,运行项目即可显示地图。项目的创建步骤如下: 创建项目,并为项目添加MapKit.framework框架。 将MapView组件添加到界面上来。 运行项目,结果如下图所示。 1.2.2 使用代码显示地图 通过添加MapView控件只能显示简单的地图,要想实现一些其他功能,我们需要在代码中实例化MKMapView,通过改变该实例中的一些属性,或者调用其中的一些方法来实现其他功能,下面的程序是使用MKMapView和UISegmentControl来切换地图的三种显示模式。实现步骤如下: 创建项目,并添加MapKit.framework框架 在.h文件中声明MKMapView属性,为UISegmentControl添加值改变事件。 #import <UIKit/UIKit.h> #import <MapKit/MapKit.h> @interface AmakerViewController : UIViewController - (IBAction)change:(id)sender; @property (strong, nonatomic) IBOutlet MKMapView *myMapView; 在.m文件中实现事件,通过改变MKMapView的mapType属性来改变显示类型。 - (IBAction)change:(id)sender { UISegmentedControl *sc = (UISegmentedControl*)sender; switch (sc.selectedSegmentIndex) { case 0: self.myMapView.mapType = MKMapTypeStandard; break; case 1: self.myMapView.mapType = MKMapTypeSatellite; break; case 2: self.myMapView.mapType = MKMapTypeHybrid; break; default: break; } } 程序运行结果如下图所示。 1.3 应用MKMapView的代理MKMapViewDelegate MKMapViewDelegate协议中定义了一些监控MapView更新的相关消息,例如,地图位置的改变、地图数据的加载、用户位置的跟踪、标记视图的管理等信息。下面是该代理的方法分类: 响应地图位置的改变 – mapView:regionWillChangeAnimated: – mapView:regionDidChangeAnimated: 响应加载地图 – mapViewWillStartLoadingMap: – mapViewDidFinishLoadingMap: – mapViewDidFailLoadingMap:withError: 用户位置的跟踪 – mapViewWillStartLocatingUser: – mapViewDidStopLocatingUser: – mapView:didUpdateUserLocation: – mapView:didFailToLocateUserWithError: – mapView:didChangeUserTrackingMode:animated: 标记视图的管理 – mapView:viewForAnnotation: – mapView:didAddAnnotationViews: – mapView:annotationView:calloutAccessoryControlTapped: – mapView:annotationView:didChangeDragState:fromOldState: – mapView:didSelectAnnotationView: – mapView:didDeselectAnnotationView: 图层的管理 – mapView:viewForOverlay: – mapView:didAddOverlayViews: 下面的程序实现了,使用MKMapViewDelegate协议监控地图位置的变化和地图的加载,实现步骤如下所示。 创建项目,添加MapKit.framework框架。 为.h文件实现MKMapViewDelegate协议。 在界面上添加MapView组件,添加属性,并建立连接。 为MapView的delegate属性指定值为self。 #import <MapKit/MapKit.h> @interface AmakerViewController : UIViewController<MKMapViewDelegate> @property (strong, nonatomic) IBOutlet MKMapView *myMapView; - (void)viewDidLoad { [super viewDidLoad]; self.myMapView.delegate = self; } 通过NSLog打印方法名称,查看方法的调用时机。 - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{ NSLog(@"regionWillChangeAnimated........"); } - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{ NSLog(@"regionDidChangeAnimated........"); } - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView{ NSLog(@"mapViewWillStartLoadingMap........"); } - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView{ NSLog(@"mapViewDidFinishLoadingMap........"); } - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error{ NSLog(@"mapViewDidFailLoadingMap........"); } 程序运行结果如下所示。 1.4 使用CLLocationManager获得设备当前经纬度信息 几乎所有的苹果设备都有GPS模块,通过GPS模块可以获得设备的当前位置信息,可以通过CLLocationManager和其代理类CLLocationManagerDelegate来获得启动和停止跟踪,并获得设备当前经的纬度信息。另外,还可以为设备进入某个特定区域做出提示。通过下面的程序,当用户点击按钮,开始跟踪设备,并通过UILabel实时显示当前设备的经纬度信息。实现步骤如下所示。 创建项目并为项目添加CoreLocation.framework框架。 在界面上添加UIButton和UILabel组件。 在.h中实现CLLocationManagerDelegate代理,声明CLLocationManager属性和UILabel属性,并声明UIButton的点击事件方法。 #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> @interface AmakerViewController : UIViewController<CLLocationManagerDelegate> - (IBAction)start:(id)sender; @property (strong, nonatomic) IBOutlet UILabel *myLocatoinInfo; @property (strong,nonatomic) CLLocationManager *lm; @end 在viewDidLoad方法中判断定位服务是否可以利用,实例化并指定属性。 - (void)viewDidLoad { [super viewDidLoad]; if ([CLLocationManager locationServicesEnabled]) { self.lm = [[CLLocationManager alloc]init]; self.lm.delegate = self; // 最小距离 self.lm.distanceFilter=kCLDistanceFilterNone; }else{ NSLog(@"定位服务不可利用"); } } 在CLLocationManagerDelegate的更新方法中实时获得最新位置信息,并显示在UILabel中。 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ self.myLocatoinInfo.text = [NSString stringWithFormat:@"[%f,%f]",newLocation.coordinate.latitude,newLocation.coordinate.longitude]; } 在UIButton的点击事件中启动跟踪。 - (IBAction)start:(id)sender { if (self.lm!=nil) { [self.lm startUpdatingLocation]; } } 程序的运行结果如下图所示。 1.5 在地图上标注位置 有时候我们知道了经纬度信息,需要在地图上标注该位置,下面的程序就实现了这一功能,当我们点击按钮时,程序根据设定的经纬度信息,使用大头针标记,标记并移动到当前位置。程序实现步骤如下所示。 创建项目,并为项目添加CoreLocation.framework和MapKit.framework框架。 创建一个标记类,并实现MKAnnotation协议,通过初始化方法为其坐标、标题和子标题赋值。 #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface MyAnnotation : NSObject<MKAnnotation> @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; @property (nonatomic, readonly, copy) NSString *title; @property (nonatomic, readonly, copy) NSString *subtitle; -(id)initWith:(CLLocationCoordinate2D)coordiante andTitle:(NSString*)myTitle andSubTitle:(NSString*)mySubTitle; @end -(id)initWith:(CLLocationCoordinate2D)myCoordinate andTitle:(NSString *)myTitle andSubTitle:(NSString *)mySubTitle{ self = [super init]; if (self) { \_coordinate = myCoordinate; \_title = myTitle; \_subtitle = mySubTitle; } return self; } 在界面上添加按钮和MapView,为MapView添加对应属性并连接,为按钮添加单击事件。 @property (strong, nonatomic) IBOutlet MKMapView *myMapView; - (IBAction)pin:(id)sender; 在按钮的单击事件中为MKMapView添加标记,并移动到当前位置。 - (IBAction)pin:(id)sender { CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.5, 110.6); MyAnnotation *ann = [[MyAnnotation alloc]initWith:coord andTitle:@"My Title" andSubTitle:@"My Sub Title"]; [self.myMapView addAnnotation:ann]; //移动到该位置并设置Level MKCoordinateRegion region; region.center.latitude = 30.5; region.center.longitude = 110.6; region.span.latitudeDelta = 10; region.span.longitudeDelta = 10; self.myMapView.region = region; } 运行程序结果如下所示。 1.6 使用CLGeocoder将位置描述转换为经纬度 很多时候我们需要将位置描述转换为经纬度,例如,用户想要查找某个位置。CLGeocoder可以将位置描述转换为经纬度信息,相反也可以将经纬度转换为位置描述(下一节我们将讨论这个问题)。 下面我们通过一个实例来讲解这方面的用法,该实例要求用户输入要查找的位置信息,点击定位按钮,在地图上显示当前位置信息。实例步骤如下所示: 创建项目并添加MapKit.framework和CoreLocation.framework框架。 在界面上添加输入框架UITextField,定位按钮UIButton和地图MapView。 在.h文件中定义UITextField输入框属性、UIButton属性、CLGeocoder属性并定义一个按钮点击事件方法。 #import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> @interface AmakerViewController : UIViewController @property (strong, nonatomic) IBOutlet UITextField *addressInfo; @property (strong, nonatomic) IBOutlet MKMapView *myMapView; @property(strong,nonatomic)CLGeocoder *geoCoder; - (IBAction)locate:(id)sender; @end 在按钮单击事件方法中实例化CLGeocoder,并调用CLGeocoder的geocoderAddressString方法,在该回调方法中获得当前位置描述的经纬度,并在地图上显示。 - (IBAction)locate:(id)sender { self.geoCoder = [[CLGeocoder alloc]init]; [self.geoCoder geocodeAddressString:self.addressInfo.text completionHandler:^(NSArray *placemarks, NSError *error) { if ([placemarks count]>0&&error==nil) { CLPlacemark *mark = [placemarks objectAtIndex:0]; double lat = mark.location.coordinate.latitude; double lon = mark.location.coordinate.longitude; CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(lat,lon); MyAnnotation *ann = [[MyAnnotation alloc]initWith:coord andTitle:@"My Title" andSubTitle:@"My Sub Title"]; [self.myMapView addAnnotation:ann]; //移动到该位置并设置Level MKCoordinateRegion region; region.center.latitude = lat; region.center.longitude = lon; region.span.latitudeDelta = 10; region.span.longitudeDelta = 10; self.myMapView.region = region; } }]; [self.addressInfo resignFirstResponder]; } 程序运行结果如下所示。 1.7 使用CLGeocoder将经纬度转换为位置描述 上一节的实例是将位置描述转换为经纬度,这一节我们将经纬度转换为位置描述,该转换过程也是通过CLGeocoder实现的。下面的实例演示了这个转换过程,首先,获得设备当前的经纬度,再将经纬度转换为位置描述。实现步骤如下所示。 创建项目添加MapKit.framework和CoreLocation.framework框架。 在界面上添加一个按钮和一个UILabel。 .h实现CLLocationManagerDelegate协议。 在.h中声明UILabel、CLLocationManager、CLGeocoder实例属性和经纬度属性及按钮单击事件方法。 #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> @interface AmakerViewController : UIViewController<CLLocationManagerDelegate> @property (strong, nonatomic) IBOutlet UILabel *addressInfo; @property(strong,nonatomic) CLLocationManager *lm; @property(strong,nonatomic) CLGeocoder *geoCoder; @property(nonatomic)double lat,lon; - (IBAction)locate:(id)sender; @end 在viewDidLoad方法中实例化CLLocationManager和CLGeocoder实例。 - (void)viewDidLoad { [super viewDidLoad]; if ([CLLocationManager locationServicesEnabled]) { self.lm = [[CLLocationManager alloc]init]; self.lm.delegate = self; } self.geoCoder = [[CLGeocoder alloc]init]; } 在CLLocationManagerDelegate协议方法- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation中获得当前设备经纬度。 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ self.lat = newLocation.coordinate.latitude; self.lon = newLocation.coordinate.longitude; } 在按钮单击方法中,将当前设备经纬度转换为位置描述。 - (IBAction)locate:(id)sender { CLLocation *l = [[CLLocation alloc]initWithLatitude:self.lat longitude:self.lon]; [self.geoCoder reverseGeocodeLocation:l completionHandler:^(NSArray *placemarks, NSError *error) { //ddd if ([placemarks count]>0&&error==nil) { CLPlacemark *mark= [placemarks objectAtIndex:0]; self.addressInfo.text = [NSString stringWithFormat:@"%@,%@",mark.country,mark.locality]; } }]; } 程序运行结果如下所示。 1.8 使用Google Place API 查询周边位置信息 如果我们想使用地图的更高级应用,例如,查询周边的宾馆、加油站、商店等信息,我们可以使用谷歌的Google Place API,该API提供了iOS地图应用的高级功能。下面URL是该API的开发文档:http://code.google.com/apis/maps/documentation/places/。iOS客户端程序需要申请一个ID URL如下:http://code.google.com/apis/maps/documentation/webservices/index.html#URLSigning。另外,该API提供了一个演示程序如下图所示。 iOS 多线程 iOS 手势处理