1. 实现一次定位
1). 导入头文件
#import <CoreLocation/CoreLocation.h>
2). 实现
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController () <CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *mgr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建CLLocationManager
self.mgr = [CLLocationManager new];
// 2. 请求用户授权,还需配置Plist,配置键为NSLocationWhenInUseUsageDescription,值为提示用户的信息
if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.mgr requestWhenInUseAuthorization];
}
// 3. 设置代理
self.mgr.delegate = self;
// 4. 调用开始方法定位
[self.mgr startUpdatingLocation];
}
#pragma mark 代理方法
// 当完成位置更新的时候调用,会频繁调用
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
// 获取位置信息
CLLocation *location = locations.lastObject;
NSLog(@"locations: %@", location);
// 5. 停止定位
[self.mgr stopUpdatingLocation];
// locations: <+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 1/19/18, 3:19:43 PM China Standard Time
}
@end
2. 持续定位
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController () <CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *mgr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建CLLocationManager
self.mgr = [CLLocationManager new];
// 请求永久权限,还需配置Plist,配置键为NSLocationAlwaysUsageDescription,值为提示用户的信息
if ([self.mgr respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[self.mgr requestAlwaysAuthorization];
}
// 设置代理
self.mgr.delegate = self;
// 开始定位
[self.mgr startUpdatingLocation];
// 设置位置筛选器,节省电量
self.mgr.distanceFilter = 10;
// 设置期望精度
self.mgr.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
// 期望精度可选值
// kCLLocationAccuracyBest;
// kCLLocationAccuracyNearestTenMeters;
// kCLLocationAccuracyHundredMeters;
// kCLLocationAccuracyKilometer;
// kCLLocationAccuracyThreeKilometers;
}
#pragma mark 代理方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *location = locations.lastObject;
NSLog(@"---%@", location);
}
@end
3. 临时授权及位置差
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController () <CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *mgr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mgr = [CLLocationManager new];
// 请求定位权限,还需配置Plist,配置键为NSLocationWhenInUseUsageDescription,值为提示用户的信息
if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.mgr requestWhenInUseAuthorization];
}
// 请求临时授权, 先判断iOS系统版本。需要配置plist,新增UIBackgroundModes键,增加后时一个数组,然后第一项名字设置为"App registers for location updates"
if ([UIDevice currentDevice].systemVersion.floatValue >= 9.0) {
// 变现为应用程序进入后台时可以继续更新当前位置
self.mgr.allowsBackgroundLocationUpdates = YES;
}
self.mgr.delegate = self;
[self.mgr startUpdatingLocation];
// 计算两点之间的位置
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:40 longitude:116];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:34.27 longitude:108.93];
// 比较的是直线距离
CLLocationDistance distance = [location1 distanceFromLocation:location2];
NSLog(@"distance: %f", distance);
}
#pragma mark 代理方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *location = locations.lastObject;
NSLog(@"latitude: %f, longitude: %f", location.coordinate.latitude, location.coordinate.longitude);
}
@end
4. 地理编码和反地理编码
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self encode];
[self decode];
}
// 地理编码
- (void)encode {
// 1. 创建一个CLGeocoder对象
CLGeocoder *geocoder = [CLGeocoder new];
// 2. 实现地理编码
[geocoder geocodeAddressString:@"乌鲁木齐" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
// 处理出错
if (error || placemarks.count == 0) {
NSLog(@"解析出错");
return;
}
// 遍历数组
for (CLPlacemark *placeMark in placemarks) {
NSLog(@"latitude: %f", placeMark.location.coordinate.latitude);
NSLog(@"longitude: %f", placeMark.location.coordinate.longitude);
NSLog(@"name: %@", placeMark.name);
// latitude: 43.792818
// longitude: 87.617733
// name: Xinjiang Urumqi
}
}];
}
// 反地理编码
- (void)decode {
// 1. 创建CLGeocoder对象
CLGeocoder *geocoder = [CLGeocoder new];
// 2. 创建一个CLLocation对象
CLLocation *location = [[CLLocation alloc] initWithLatitude:43.792818 longitude:87.617733];
// 3. 反地理编码
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
// 处理出错
if (error || placemarks.count == 0) {
NSLog(@"解析出错");
return;
}
// 遍历数组
for (CLPlacemark *placeMark in placemarks) {
NSLog(@"latitude: %f", placeMark.location.coordinate.latitude);
NSLog(@"longitude: %f", placeMark.location.coordinate.longitude);
NSLog(@"name: %@", placeMark.name);
// latitude: 43.794040
// longitude: 87.620584
// name: No.37 Zhongshan Road
}
}];
}
@end
5. MapView使用
1). 导入Mapkit.framework
点击工程->General->Linked Frameworks and Libraries,输入MapKit,点击Add。
2). 示例
/**
#pragma mark 1. 显示用户位置 (掌握)
#pragma mark 2. 设置地图显示类型 (掌握)
#pragma mark 3. 根据用户位置显示对应的大头针信息(掌握)
#pragma mark 4. 设置以用户所在位置为中心点(掌握)
#pragma mark 5. 获取地图显示区域改变时的中心点坐标及显示跨度 (了解)
*/
#import "ViewController.h"
#import <MapKit/MapKit.h>
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (nonatomic, strong) CLLocationManager *mgr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1. 创建位置管理器
self.mgr = [CLLocationManager new];
//2. 请求授权 --> plist
if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.mgr requestWhenInUseAuthorization];
}
//3. 设置显示用户位置
//Tracking: 跟踪
/**
MKUserTrackingModeNone = 0,
MKUserTrackingModeFollow, 定位
MKUserTrackingModeFollowWithHeading, 定位并且显示方向
*/
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//4. 设置代理 --> 获取用户位置信息
self.mapView.delegate = self;
/**
iOS9新增属性
*/
//1. 设置交通状况
self.mapView.showsTraffic = YES;
//2. 设置指南针(默认就是YES), 屏幕旋转后会出现, 如果点击会校正方向
self.mapView.showsCompass = YES;
//3. 设置比例尺
self.mapView.showsScale = YES;
}
#pragma mark 切换地图类型
- (IBAction)mapTypeChangeClick:(UISegmentedControl *)sender {
/**
MKMapTypeStandard = 0, 默认
MKMapTypeSatellite, 卫星
MKMapTypeHybrid, 混合
MKMapTypeSatelliteFlyover NS_ENUM_AVAILABLE(10_11, 9_0), 下面两个属性, 中国区无用
MKMapTypeHybridFlyover NS_ENUM_AVAILABLE(10_11, 9_0),
*/
// 设置地图类型 , 一般要使用默认, 要么使用混个, 单纯的卫星图没有意义.
switch (sender.selectedSegmentIndex) {
case 0:
self.mapView.mapType = MKMapTypeStandard;
break;
case 1:
self.mapView.mapType = MKMapTypeSatellite;
break;
case 2:
self.mapView.mapType = MKMapTypeHybrid;
break;
case 3:
self.mapView.mapType = MKMapTypeSatelliteFlyover;
break;
case 4:
self.mapView.mapType = MKMapTypeHybridFlyover;
break;
default:
break;
}
}
/**
完成用户位置更新的时候 调用
MKUserLocation : 大头针模型
*/
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
//需求: 显示大头针信息 --> 反地理编码实现
//1. 创建一个CLGeocoder对象
CLGeocoder *geocoder = [CLGeocoder new];
//2. 实现反地理编码方法
[geocoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
//3.1 防错处理
if (placemarks.count == 0 || error) {
return;
}
//3.2 获取对象
CLPlacemark *placemark = placemarks.lastObject;
//3.3 设置标题为城市信息
userLocation.title = placemark.locality;
//3.4 设置子标题为详细地址
userLocation.subtitle = placemark.name;
}];
}
#pragma mark 点击此按钮, 返回用户所在的位置
- (IBAction)backUserLocationClick:(id)sender {
//1. 设置中心点坐标
//self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;
//2. 设置范围的属性
//2.1 获取坐标
CLLocationCoordinate2D coordinate = self.mapView.userLocation.location.coordinate;
//2.2 设置显示范围 --> 为了跟系统默认的跨度保持一致, 我们可以打印region的span值来获取, 然后设置即可
//1° ~ 111KM
MKCoordinateSpan span = MKCoordinateSpanMake(0.021252, 0.014720);
//2.3 设置范围属性 默认没有动画
//self.mapView.region = MKCoordinateRegionMake(coordinate, span);
//设置范围方法 可以设置动画
[self.mapView setRegion:MKCoordinateRegionMake(coordinate, span) animated:YES];
}
/**
当地图显示区域发生改变后, 会调用的方法
*/
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
//获取默认的显示大小 --> span
NSLog(@"latitudeDelta : %f, longitudeDelta: %f", mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta);
// latitudeDelta : 0.021252, longitudeDelta: 0.014720
// latitudeDelta : 0.010626, longitudeDelta: 0.007360
// latitudeDelta : 0.005313, longitudeDelta: 0.003680
}
#pragma mark 放大地图
- (IBAction)zoomInClick:(id)sender {
//1. Delta 跨度缩小一倍
//region : 是当前地图显示的区域
CGFloat latitudeDelta = self.mapView.region.span.latitudeDelta * 0.5;
CGFloat longitudeDelta = self.mapView.region.span.longitudeDelta * 0.5;
//2. 重设region属性
[self.mapView setRegion: MKCoordinateRegionMake(self.mapView.centerCoordinate, MKCoordinateSpanMake(latitudeDelta, longitudeDelta)) animated:YES];
}
#pragma mark 缩小地图
- (IBAction)zoomOutClick:(id)sender {
//1. Delta 跨度放大一倍
//region : 是当前地图显示的区域
CGFloat latitudeDelta = self.mapView.region.span.latitudeDelta * 2;
CGFloat longitudeDelta = self.mapView.region.span.longitudeDelta * 2;
//2. 重设region属性
[self.mapView setRegion: MKCoordinateRegionMake(self.mapView.region.center, MKCoordinateSpanMake(latitudeDelta, longitudeDelta)) animated:YES];
}
@end
6. 点击添加大头针
1). 新建类MyAnnotationModel,并实现协议MKAnnotation,属性从协议中拷贝,并删除readonly。
/**
1. 导入框架 MapKit
2. 遵守协议 MKAnnotation
3. 设置属性 直接去协议中拷贝-->删掉readonly
*/
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MyAnnotationModel : NSObject<MKAnnotation>
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy, nullable) NSString *title;
@property (nonatomic, copy, nullable) NSString *subtitle;
@end
2). MapView中点击添加大头针
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import "MyAnnotationModel.h"
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
/** 位置管理器*/
@property (nonatomic, strong) CLLocationManager *mgr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1. 创建位置管理器
self.mgr = [CLLocationManager new];
//2. 请求授权
if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.mgr requestWhenInUseAuthorization];
}
//3. 跟踪用户位置
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//4. 设置地图的代理
self.mapView.delegate = self;
}
#pragma mark 地图的代理方法
/**
只要添加了大头针模型, 就会来到这个方法, 设置并返回对应的View
*/
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//如果返回nil, 就代表用户没有自定义的需求, 所有的View样式由系统处理
//MKUserLocation: 系统专门显示用户位置的大头针模型
//MyAnnotationModel: 自定义的类
//1. 如果发现是显示用户位置的大头针模型, 就返回nil
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
//2. 自定义大头针View --> 跟Cell的创建几乎一样
static NSString *ID = @"annoView";
//MKAnnotationView : 默认image属性没有赋值
//MKPinAnnotationView : 子类是默认有View的
MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];
if (annoView == nil) {
annoView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
/**
MKPinAnnotationColorRed
MKPinAnnotationColorGreen,
MKPinAnnotationColorPurple
*/
//3. 设置颜色, iOS9新增
annoView.pinTintColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1];
//4. 设置动画掉落
annoView.animatesDrop = YES;
}
return annoView;
}
#pragma mark 点击添加大头针
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1. 获取点击地图的点
CGPoint point = [[touches anyObject] locationInView:self.mapView];
//2. 将点击的点转换成经纬度
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
//3. 添加大头针
MyAnnotationModel *annotationModel = [MyAnnotationModel new];
annotationModel.coordinate = coordinate;
annotationModel.title = @"北京市";
annotationModel.subtitle = @"北京市一个迷人的城市";
[self.mapView addAnnotation:annotationModel];
}
@end
3). 自定义大头针图标
- 为自定义的大头针添加属性
/** 图像属性*/
@property (nonatomic, copy, nullable) NSString *icon;
- 在返回自定义大头针的时候为属性赋值
#pragma mark 地图的代理方法
/**
只要添加了大头针模型, 就会来到这个方法, 设置并返回对应的View
*/
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//如果返回nil, 就代表用户没有自定义的需求, 所有的View样式由系统处理
//MKUserLocation: 系统专门显示用户位置的大头针模型
//MyAnnotationModel: 自定义的类
//1. 如果发现是显示用户位置的大头针模型, 就返回nil
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
//2. 自定义大头针View --> 跟Cell的创建几乎一样
static NSString *ID = @"annoView";
//MKAnnotationView : 默认image属性没有赋值
//MKPinAnnotationView : 子类是默认有View的
MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:ID];
if (annoView == nil) {
annoView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
// 设置图像 --> MVC
//annoView.image = [UIImage imageNamed:@"苍老师"];
}
// 设置图像 --> MVC
MyAnnotationModel *myAnnotation = annotation;
annoView.image = [UIImage imageNamed:myAnnotation.icon];
return nil;
}
- 在大头针图像出现之前设置图片位置
#pragma mark 此方法在添加大头针的时候就会调用, 并且, 在图像出现之前
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray<MKAnnotationView *> *)views
{
//NSLog(@"count: %zd",views.count);
for (MKAnnotationView * annoView in views) {
//0. 处理显示用户位置的大头针View, 不要增加动画
if ([annoView.annotation isKindOfClass:[MKUserLocation class]]) {
continue;
}
//1. 记录原本的位置
CGRect endFrame = annoView.frame;
//2. 将View的Y值改为0, 重设Frame
annoView.frame = CGRectMake(endFrame.origin.x, 0, endFrame.size.width, endFrame.size.height);
//3. 将位置还原, 执行动画效果
[UIView animateWithDuration:0.25 animations:^{
annoView.frame = endFrame;
}];
}
}
7. 地图导航-调用系统
#import "ViewController.h"
#import <MapKit/MapKit.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *destinationTF;
@end
@implementation ViewController
#pragma mark 导航按钮点击
- (IBAction)navigateClick:(id)sender {
//1. 创建CLGeocoder对象
CLGeocoder *geocoder = [CLGeocoder new];
//2. 调用地理编码方法
[geocoder geocodeAddressString:self.destinationTF.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
//3 防错处理
if (placemarks.count == 0 || error) {
NSLog(@"您输入的地址我们没找到!!!");
return;
}
//遍历数组获取数据 --> 正地理编码, 可能重名, 所以数组数量大于1, 一定要给列表提示用户选择
NSMutableArray *arr = [NSMutableArray array];
for (CLPlacemark *pm in placemarks) {
//4. 获取地表对象 暂取最后一个
//5. 创建MKPlacemark对象
MKPlacemark *mkpm = [[MKPlacemark alloc] initWithPlacemark:pm];
//6. 创建一个MKMapItem
MKMapItem *destItem = [[MKMapItem alloc] initWithPlacemark:mkpm];
[arr addObject:destItem];
}
//7. 调用open类方法, 打开导航
//WithItems: 传入要定位的点
//launchOptions: 导航参数
NSDictionary *options = @{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsMapTypeKey : @(MKMapTypeHybrid), MKLaunchOptionsShowsTrafficKey : @(YES)};
[MKMapItem openMapsWithItems:arr launchOptions:nil];
}];
}
@end
8. 地图导航-自己画线
#import "ViewController.h"
#import <MapKit/MapKit.h>
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
/** 位置管理器--> 要定位必须用这个属性*/
@property (nonatomic, strong) CLLocationManager *mgr;
@end
/**
如果是地图画线, 无法在模拟器中运行 --> iOS8以后无法在模拟器中运行
*/
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建位置管理器并授权
self.mgr = [CLLocationManager new];
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
[self.mgr requestWhenInUseAuthorization];
}
// 设置代理
self.mapView.delegate = self;
}
#pragma mark 导航按钮点击
- (IBAction)navigateClick:(id)sender {
// 回收键盘
[self.view endEditing:YES];
//1. 创建CLGeocoder对象
CLGeocoder *geocoder = [CLGeocoder new];
//2. 调用地理编码方法
[geocoder geocodeAddressString:"西山" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
//3 防错处理
if (placemarks.count == 0 || error) {
return;
}
//遍历数组获取数据 --> 正地理编码, 可能重名, 所以数组数量大于1, 一定要给列表提示用户选择
//4. 获取地表对象 暂取最后一个
CLPlacemark *pm = placemarks.lastObject;
//5. 创建MKPlacemark对象
MKPlacemark *mkpm = [[MKPlacemark alloc] initWithPlacemark:pm];
//6.1 创建一个终点MKMapItem
MKMapItem *destinationItem = [[MKMapItem alloc] initWithPlacemark:mkpm];
//6.2 创建一个起点MKMapItem
MKMapItem *souceItem = [MKMapItem mapItemForCurrentLocation];
//7. 创建一个方向请求对象 --> 相当于拼接网址 --> 需要传入参数: 起点和终点
MKDirectionsRequest *request = [MKDirectionsRequest new];
//7.1 设置终点
request.destination = destinationItem;
//7.2 设置起点
request.source = souceItem;
//8. 创建方向对象 --> 创建一个请求对象
MKDirections *direction = [[MKDirections alloc] initWithRequest:request];
//9. 计算路径 --> 处理网络请求的结果
[direction calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
//10.1 防错处理
if (response.routes.count == 0 || error) {
NSLog(@"没有找到对应的路线");
return ;
}
//10.2 遍历数组, 获取数据
//MKRoute : 路线对象
for (MKRoute *route in response.routes) {
//polyline : 多段线
//11. 获取折线信息
MKPolyline *polyline = route.polyline;
//12. 添加到地图上
//Overlay : 遮盖物
[self.mapView addOverlay:polyline];
}
}];
}];
}
#pragma mark 如果添加了遮盖物, 就需要调用此方法, 来设置渲染
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
//1. 创建一个折线渲染物对象
MKPolylineRenderer *polyline = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
polyline.fillColor = [UIColor redColor];
//2. 设置线条颜色 --> 必须设置, 否则看不见
// polyline.strokeColor = [UIColor blueColor];
//3. 设置线条宽度
polyline.lineWidth = 10;
return polyline;
}
@end