iOS GPS 定位应用


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框架的,所以在使用之前必须添加这两个框架,并导入必要的头文件。

实现步骤如下:

  1. 选择项目名称。
  2. 在右边选择TARGETS。
  3. 在右边选择Build Phases。
  4. 在下面的Link Binary With Libraries中点击"+"找到这两个框架并添加之。
  5. 在头文件中包含这两个框架的头文件,代码如下:
#import <UIKit/UIKit.h>

#import <MapKit/MapKit.h>

#import <CoreLocation/CoreLocation.h>

@interface AmakerViewController : UIViewController

@end

添加过程如下图所示:

图26.1 为项目添加框架

1.2 使用MKMapView显示地图

iOS中显示地图非常简单,最简单的做法是在界面中添加MapView组件运行项目即可显示地图,但是,大部分情况下我们还是需要自己定义一些功能来显示地图的,例如,改变地图的显示类型,可以显示标准、卫星和混合等不同类型的地图。

1.2.1 使用MapView组件直接显示地图

使用MapView显示地图非常简单只要将MapView组件放在界面上,运行项目即可显示地图。项目的创建步骤如下:

  1. 创建项目,并为项目添加MapKit.framework框架。
  2. 将MapView组件添加到界面上来。
  3. 运行项目,结果如下图所示。

图26.2 使用MapView组件直接显示地图

1.2.2 使用代码显示地图

通过添加MapView控件只能显示简单的地图,要想实现一些其他功能,我们需要在代码中实例化MKMapView,通过改变该实例中的一些属性,或者调用其中的一些方法来实现其他功能,下面的程序是使用MKMapView和UISegmentControl来切换地图的三种显示模式。实现步骤如下:

  1. 创建项目,并添加MapKit.framework框架
  2. 在.h文件中声明MKMapView属性,为UISegmentControl添加值改变事件。
#import <UIKit/UIKit.h>

#import <MapKit/MapKit.h>

@interface AmakerViewController : UIViewController

- (IBAction)change:(id)sender;

@property (strong, nonatomic) IBOutlet MKMapView *myMapView;
  1. 在.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. 程序运行结果如下图所示。

图26.3 使用MKMapView显示不同类型地图

1.3 应用MKMapView的代理MKMapViewDelegate

MKMapViewDelegate协议中定义了一些监控MapView更新的相关消息,例如,地图位置的改变、地图数据的加载、用户位置的跟踪、标记视图的管理等信息。下面是该代理的方法分类:

  1. 响应地图位置的改变
 mapView:regionWillChangeAnimated:

 mapView:regionDidChangeAnimated:
  1. 响应加载地图
 mapViewWillStartLoadingMap:

 mapViewDidFinishLoadingMap:

 mapViewDidFailLoadingMap:withError:
  1. 用户位置的跟踪
 mapViewWillStartLocatingUser:

 mapViewDidStopLocatingUser:

 mapView:didUpdateUserLocation:

 mapView:didFailToLocateUserWithError:

 mapView:didChangeUserTrackingMode:animated:
  1. 标记视图的管理
 mapView:viewForAnnotation:

 mapView:didAddAnnotationViews:

 mapView:annotationView:calloutAccessoryControlTapped:

 mapView:annotationView:didChangeDragState:fromOldState:

 mapView:didSelectAnnotationView:

 mapView:didDeselectAnnotationView:
  1. 图层的管理
 mapView:viewForOverlay:

 mapView:didAddOverlayViews:

下面的程序实现了,使用MKMapViewDelegate协议监控地图位置的变化和地图的加载,实现步骤如下所示。

  1. 创建项目,添加MapKit.framework框架。
  2. 为.h文件实现MKMapViewDelegate协议。
  3. 在界面上添加MapView组件,添加属性,并建立连接。
  4. 为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;

}
  1. 通过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. 程序运行结果如下所示。

图26.4 使用MKMapViewDelegate的应用

1.4 使用CLLocationManager获得设备当前经纬度信息

几乎所有的苹果设备都有GPS模块,通过GPS模块可以获得设备的当前位置信息,可以通过CLLocationManager和其代理类CLLocationManagerDelegate来获得启动和停止跟踪,并获得设备当前经的纬度信息。另外,还可以为设备进入某个特定区域做出提示。通过下面的程序,当用户点击按钮,开始跟踪设备,并通过UILabel实时显示当前设备的经纬度信息。实现步骤如下所示。

  1. 创建项目并为项目添加CoreLocation.framework框架。
  2. 在界面上添加UIButton和UILabel组件。
  3. 在.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
  1. 在viewDidLoad方法中判断定位服务是否可以利用,实例化并指定属性。
- (void)viewDidLoad

{

    [super viewDidLoad];

 if ([CLLocationManager locationServicesEnabled]) {

        self.lm = [[CLLocationManager alloc]init];

        self.lm.delegate = self;

        // 最小距离

        self.lm.distanceFilter=kCLDistanceFilterNone;

    }else{

        NSLog(@"定位服务不可利用");

    }

}
  1. 在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];

}
  1. 在UIButton的点击事件中启动跟踪。
- (IBAction)start:(id)sender {

    if (self.lm!=nil) {

        [self.lm startUpdatingLocation];

    }

}
  1. 程序的运行结果如下图所示。

图26.5 获得当前设备位置信息

1.5 在地图上标注位置

有时候我们知道了经纬度信息,需要在地图上标注该位置,下面的程序就实现了这一功能,当我们点击按钮时,程序根据设定的经纬度信息,使用大头针标记,标记并移动到当前位置。程序实现步骤如下所示。

  1. 创建项目,并为项目添加CoreLocation.framework和MapKit.framework框架。
  2. 创建一个标记类,并实现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;

}
  1. 在界面上添加按钮和MapView,为MapView添加对应属性并连接,为按钮添加单击事件。
@property (strong, nonatomic) IBOutlet MKMapView *myMapView;

- (IBAction)pin:(id)sender;
  1. 在按钮的单击事件中为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. 运行程序结果如下所示。

图26.6 为地图添加标记

1.6 使用CLGeocoder将位置描述转换为经纬度

很多时候我们需要将位置描述转换为经纬度,例如,用户想要查找某个位置。CLGeocoder可以将位置描述转换为经纬度信息,相反也可以将经纬度转换为位置描述(下一节我们将讨论这个问题)。

下面我们通过一个实例来讲解这方面的用法,该实例要求用户输入要查找的位置信息,点击定位按钮,在地图上显示当前位置信息。实例步骤如下所示:

  1. 创建项目并添加MapKit.framework和CoreLocation.framework框架。
  2. 在界面上添加输入框架UITextField,定位按钮UIButton和地图MapView。
  3. 在.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
  1. 在按钮单击事件方法中实例化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&amp;&amp;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. 程序运行结果如下所示。

图26.7 将位置描述转换为经纬度

1.7 使用CLGeocoder将经纬度转换为位置描述

上一节的实例是将位置描述转换为经纬度,这一节我们将经纬度转换为位置描述,该转换过程也是通过CLGeocoder实现的。下面的实例演示了这个转换过程,首先,获得设备当前的经纬度,再将经纬度转换为位置描述。实现步骤如下所示。

  1. 创建项目添加MapKit.framework和CoreLocation.framework框架。
  2. 在界面上添加一个按钮和一个UILabel。
  3. .h实现CLLocationManagerDelegate协议。
  4. 在.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
  1. 在viewDidLoad方法中实例化CLLocationManager和CLGeocoder实例。
- (void)viewDidLoad

{

    [super viewDidLoad];

    if ([CLLocationManager locationServicesEnabled]) {

        self.lm = [[CLLocationManager alloc]init];

        self.lm.delegate = self;

    }

    self.geoCoder = [[CLGeocoder alloc]init];

}
  1. 在CLLocationManagerDelegate协议方法- (void)locationManager:(CLLocationManager *)manager
  2. didUpdateToLocation:(CLLocation *)newLocation中获得当前设备经纬度。
- (void)locationManager:(CLLocationManager *)manager

 didUpdateToLocation:(CLLocation *)newLocation

     fromLocation:(CLLocation *)oldLocation{

    self.lat = newLocation.coordinate.latitude;

    self.lon = newLocation.coordinate.longitude;

}
  1. 在按钮单击方法中,将当前设备经纬度转换为位置描述。
- (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&amp;&amp;error==nil) {

            CLPlacemark *mark= [placemarks objectAtIndex:0];

            self.addressInfo.text = [NSString stringWithFormat:@"%@,%@",mark.country,mark.locality];

        }

    }];

}
  1. 程序运行结果如下所示。

图26.8 将经纬度转换为位置描述

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提供了一个演示程序如下图所示。

图26.9 Google Place API 演示