@implementation HMAnnotationView
- (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier
//自定义大头针方法
{
if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) {
// 初始化
// 设置大头针标题是否显示
self.canShowCallout = YES;
// 设置大头针左边的辅助视图
self.leftCalloutAccessoryView = [[UISwitch alloc] init];
// 设置大头针右边的辅助视图
self.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd];
}
return self;
}
+ (instancetype)annotationViewWithMap:(MKMapView *)mapView
{
static NSString *identifier = @"anno";
// 1.从缓存池中取
HMAnnotationView *annoView = (HMAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
// 2.如果缓存池中没有, 创建一个新的
if (annoView == nil) {
annoView = [[HMAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:identifier];
}
return annoView;
}
//- (void)setAnnotation:(id)annotation
- (void)setAnnotation:(HMAnnotation *)annotation
{
[super setAnnotation:annotation];
// 处理自己特有的操作
self.image = [UIImage imageNamed:annotation.icon];
}
@end
//大头针的属性
@interface HMAnnotation : NSObject
/**
* 大头针的位置
*/
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
/**
* 大头针标题
*/
@property (nonatomic, copy) NSString *title;
/**
* 大头针的子标题
*/
@property (nonatomic, copy) NSString *subtitle;
/**
* 图标
*/
@property (nonatomic, copy) NSString *icon;
@end
@interface ViewController ()
/**
* 地图
*/
@property (weak, nonatomic) IBOutlet MKMapView *customMapView;
@property (nonatomic, strong) CLLocationManager *mgr;
/**
* 地理编码对象
*/
@property (nonatomic ,strong) CLGeocoder *geocoder;
- (IBAction)addAnno;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 注意:在iOS8中, 如果想要追踪用户的位置, 必须自己主动请求隐私权限
if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) {
// 主动请求权限
self.mgr = [[CLLocationManager alloc] init];
[self.mgr requestAlwaysAuthorization];
}
// 设置不允许地图旋转
self.customMapView.rotateEnabled = NO;
// 成为mapVIew的代理
self.customMapView.delegate = self;
// 如果想利用MapKit获取用户的位置, 可以追踪
self.customMapView.userTrackingMode = MKUserTrackingModeFollow;
}
#pragma MKMapViewDelegate
/**
* 每次添加大头针就会调用(地图上有几个大头针就调用几次)
*
* @param mapView 地图
* @param annotation 大头针模型
*
* @return 大头针的view
*/
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation
{
// NSLog(@"%s", __func__);
NSLog(@"annotation === %@", annotation);
// 对用户当前的位置的大头针特殊处理
if ([annotation isKindOfClass:[HMAnnotation class]] == NO) {
return nil;
}
// 注意: 如果返回nil, 系统会按照自己默认的方式显示
// return nil;
/*
static NSString *identifier = @"anno";
// 1.从缓存池中取
// 注意: 默认情况下MKAnnotationView是无法显示的, 如果想自定义大头针可以使用MKAnnotationView的子类MKPinAnnotationView
// 注意: 如果是自定义的大头针, 默认情况点击大头针之后是不会显示标题的, 需要我们自己手动设置显示
// MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
// 2.如果缓存池中没有, 创建一个新的
if (annoView == nil) {
// annoView = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:identifier];
annoView = [[MKAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:identifier];
// 设置大头针的颜色
// annoView.pinColor = MKPinAnnotationColorPurple;
// 设置大头针从天而降
// annoView.animatesDrop = YES;
// 设置大头针标题是否显示
annoView.canShowCallout = YES;
// 设置大头针标题显示的偏移位
// annoView.calloutOffset = CGPointMake(-50, 0);
// 设置大头针左边的辅助视图
annoView.leftCalloutAccessoryView = [[UISwitch alloc] init];
// 设置大头针右边的辅助视图
annoView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd];
}
// 设置大头针的图片
// 注意: 如果你是使用的MKPinAnnotationView创建的自定义大头针, 那么设置图片无效, 因为系统内部会做一些操作, 覆盖掉我们自己的设置
// annoView.image = [UIImage imageNamed:@"category_4"];
HMAnnotation *anno = (HMAnnotation *)annotation;
annoView.image = [UIImage imageNamed:anno.icon];
// 3.给大头针View设置数据
annoView.annotation = annotation;
// 4.返回大头针View
return annoView;
*/
// 1.创建大头针
HMAnnotationView *annoView = [HMAnnotationView annotationViewWithMap:mapView];
// 2.设置模型
annoView.annotation = annotation;
// 3.返回大头针
return annoView;
}
/**
* 每次更新到用户的位置就会调用(调用不频繁, 只有位置改变才会调用)
*
* @param mapView 促发事件的控件
* @param userLocation 大头针模型
*/
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
// 利用反地理编码获取位置之后设置标题
[self.geocoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks firstObject];
NSLog(@"获取地理位置成功 name = %@ locality = %@", placemark.name, placemark.locality);
userLocation.title = placemark.name;
userLocation.subtitle = placemark.locality;
}];
/*
// 设置地图显示的区域
// 获取用户的位置
CLLocationCoordinate2D center = userLocation.location.coordinate;
// 指定经纬度的跨度
MKCoordinateSpan span = MKCoordinateSpanMake(5 ,5);
// 将用户当前的位置作为显示区域的中心点, 并且指定需要显示的跨度范围
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
// 设置显示区域
[self.customMapView setRegion:region animated:YES];
*/
[self.customMapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
#pragma mark - 懒加载
- (CLGeocoder *)geocoder
{
if (!_geocoder) {
_geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
- (IBAction)addAnno {
// 创建大头针模型
HMAnnotation *anno = [[HMAnnotation alloc] init];
anno.title = @"孙庄";
anno.subtitle = @"育新小区";
CGFloat latitude = 36.821199 + arc4random_uniform(20);
CGFloat longitude = 116.858776 + arc4random_uniform(20);
anno.coordinate = CLLocationCoordinate2DMake(latitude , longitude);
anno.icon = @"category_1";
// 添加大头针
[self.customMapView addAnnotation:anno];
// 创建大头针模型
HMAnnotation *anno2 = [[HMAnnotation alloc] init];
anno2.title = @"传智2";
anno2.subtitle = @"育新小区2";
CGFloat latitude2 = 36.821199 + arc4random_uniform(20);
CGFloat longitude2 = 116.858776 + arc4random_uniform(20);
anno2.coordinate = CLLocationCoordinate2DMake(latitude2 , longitude2);
anno2.icon = @"category_2";
// 添加大头针
[self.customMapView addAnnotation:anno2];
}
@end
//系统自带的APP的导航
#import "ViewController.h"
#import
@interface ViewController ()
- (IBAction)startNavigation;
/**
* 开始位置
*/
@property (weak, nonatomic) IBOutlet UITextField *startField;
/**
* 结束位置
*/
@property (weak, nonatomic) IBOutlet UITextField *endField;
/**
* 地理编码对象
*/
@property(nonatomic, strong) CLGeocoder *geocoder;
@end
@implementation ViewController
- (void)startNavigation
{
// 1.获取用户输入的起点和终点
NSString *startStr = self.startField.text;
NSString *endStr = self.endField.text;
if (startStr == nil || startStr.length == 0 ||
endStr == nil || endStr.length == 0) {
NSLog(@"请输入起点或者终点");
return;
}
// 2.利用GEO对象进行地理编码获取到地标对象(CLPlacemark )
// 2.1获取开始位置的地标
[self.geocoder geocodeAddressString:startStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 开始位置的地标
CLPlacemark *startCLPlacemark = [placemarks firstObject];
// 3. 获取结束位置的地标
[self.geocoder geocodeAddressString:endStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 结束位置的地标
CLPlacemark *endCLPlacemark = [placemarks firstObject];
// 开始导航
[self startNavigationWithstartCLPlacemark:startCLPlacemark endCLPlacemark:endCLPlacemark];
}];
}];
}
/**
* 开始导航
*
* @param startCLPlacemark 起点的地标
* @param endCLPlacemark 终点的地标
*/
- (void)startNavigationWithstartCLPlacemark:(CLPlacemark *)startCLPlacemark endCLPlacemark:(CLPlacemark *)endCLPlacemark
{
// 0.创建起点和终点
// 0.1创建起点
MKPlacemark *startPlacemark = [[MKPlacemark alloc] initWithPlacemark:startCLPlacemark];
MKMapItem *startItem = [[MKMapItem alloc] initWithPlacemark:startPlacemark];;
// 0.2创建终点
MKPlacemark *endPlacemark = [[MKPlacemark alloc] initWithPlacemark:endCLPlacemark];
MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPlacemark];
// 1. 设置起点和终点数组
NSArray *items = @[startItem, endItem];
// 2.设置启动附加参数
NSMutableDictionary *md = [NSMutableDictionary dictionary];
// 导航模式(驾车/走路)
md[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving;
// 地图显示模式
// md[MKLaunchOptionsMapTypeKey] = @(MKMapTypeHybrid);
// 只要调用MKMapItem的open方法, 就可以打开系统自带的地图APP进行导航
// Items: 告诉系统地图APP要从哪到哪
// launchOptions: 启动系统自带地图APP的附加参数(导航的模式/是否需要先交通状况/地图的模式/..)
[MKMapItem openMapsWithItems:items launchOptions:md];
}
#pragma mark - 懒加载
- (CLGeocoder *)geocoder
{
if (!_geocoder) {
self.geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
@end
//获取路线信息
@interface ViewController ()
- (IBAction)startNavigation;
/**
* 开始位置
*/
@property (weak, nonatomic) IBOutlet UITextField *startField;
/**
* 结束位置
*/
@property (weak, nonatomic) IBOutlet UITextField *endField;
/**
* 地理编码对象
*/
@property(nonatomic, strong) CLGeocoder *geocoder;
@end
@implementation ViewController
/**
* 点击开始导航按钮
*/
- (void)startNavigation
{
// 1.获取用户输入的起点和终点
NSString *startStr = self.startField.text;
NSString *endStr = self.endField.text;
if (startStr == nil || startStr.length == 0 ||
endStr == nil || endStr.length == 0) {
NSLog(@"请输入起点或者终点");
return;
}
// 2.利用GEO对象进行地理编码获取到地标对象(CLPlacemark )
// 2.1获取开始位置的地标
[self.geocoder geocodeAddressString:startStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 开始位置的地标
CLPlacemark *startCLPlacemark = [placemarks firstObject];
// 3. 获取结束位置的地标
[self.geocoder geocodeAddressString:endStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 结束位置的地标
CLPlacemark *endCLPlacemark = [placemarks firstObject];
// 开始导航
[self startDirectionsWithstartCLPlacemark:startCLPlacemark endCLPlacemark:endCLPlacemark];
}];
}];
}
/**
* 发送请求获取路线相信信息
*
* @param startCLPlacemark 起点的地标
* @param endCLPlacemark 终点的地标
*/
- (void)startDirectionsWithstartCLPlacemark:(CLPlacemark *)startCLPlacemark endCLPlacemark:(CLPlacemark *)endCLPlacemark
{
/*
MKDirectionsRequest:说清楚:从哪里 --> 到哪里
MKDirectionsResponse:从哪里 --> 到哪里 :的具体路线信息
*/
// -1.创建起点和终点对象
// -1.1创建起点对象
MKPlacemark *startMKPlacemark = [[MKPlacemark alloc] initWithPlacemark:startCLPlacemark];
MKMapItem *startItem = [[MKMapItem alloc] initWithPlacemark:startMKPlacemark];
// -1.2创建终点对象
MKPlacemark *endMKPlacemark = [[MKPlacemark alloc] initWithPlacemark:endCLPlacemark];
MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endMKPlacemark];
// 0.创建request对象
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// 0.1设置起点
request.source = startItem;
// 0.2设置终点
request.destination = endItem;
// 1.发送请求到苹果的服务器获取导航路线信息
// 接收一个MKDirectionsRequest请求对象, 我们需要在该对象中说清楚:
// 从哪里 --> 到哪里
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
// 2.计算路线信息, 计算完成之后会调用blcok
// 在block中会传入一个响应者对象(response), 这个响应者对象中就存放着路线信息
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
// 打印获取到的路线信息
// 2.1获取所有的路线
NSArray *routes = response.routes;
for (MKRoute *route in routes) {
NSLog(@"%f千米 %f小时", route.distance / 1000, route.expectedTravelTime/ 3600);
NSArray *steps = route.steps;
for (MKRouteStep *step in steps) {
NSLog(@"%@ %f", step.instructions, step.distance);
}
}
}];
}
#pragma mark - 懒加载
- (CLGeocoder *)geocoder
{
if (!_geocoder) {
self.geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
@end
//路线的选择代码
#import "ViewController.h"
#import
#import "HMAnnotation.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet MKMapView *mapVIew;
/**
* 地理编码对象
*/
@property(nonatomic, strong) CLGeocoder *geocoder;
- (IBAction)drawLine;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapVIew.delegate = self;
}
/**
* 点击开始导航按钮
*/
- (IBAction)drawLine
{
// 1.获取用户输入的起点和终点
NSString *startStr = @"北京";
NSString *endStr = @"云南";
if (startStr == nil || startStr.length == 0 ||
endStr == nil || endStr.length == 0) {
NSLog(@"请输入起点或者终点");
return;
}
// 2.利用GEO对象进行地理编码获取到地标对象(CLPlacemark )
// 2.1获取开始位置的地标
[self.geocoder geocodeAddressString:startStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 开始位置的地标
CLPlacemark *startCLPlacemark = [placemarks firstObject];
// 添加起点的大头针
HMAnnotation *startAnno = [[HMAnnotation alloc ] init];
startAnno.title = startCLPlacemark.locality;
startAnno.subtitle = startCLPlacemark.name;
startAnno.coordinate = startCLPlacemark.location.coordinate;
[self.mapVIew addAnnotation:startAnno];
// 3. 获取结束位置的地标
[self.geocoder geocodeAddressString:endStr completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count == 0) return;
// 结束位置的地标
CLPlacemark *endCLPlacemark = [placemarks firstObject];
// 添加终点的大头针
HMAnnotation *endAnno = [[HMAnnotation alloc ] init];
endAnno.title = endCLPlacemark.locality;
endAnno.subtitle = endCLPlacemark.name;
endAnno.coordinate = endCLPlacemark.location.coordinate;
[self.mapVIew addAnnotation:endAnno];
// 开始导航
[self startDirectionsWithstartCLPlacemark:startCLPlacemark endCLPlacemark:endCLPlacemark];
}];
}];
}
/**
* 发送请求获取路线相信信息
*
* @param startCLPlacemark 起点的地标
* @param endCLPlacemark 终点的地标
*/
- (void)startDirectionsWithstartCLPlacemark:(CLPlacemark *)startCLPlacemark endCLPlacemark:(CLPlacemark *)endCLPlacemark
{
/*
MKDirectionsRequest:说清楚:从哪里 --> 到哪里
MKDirectionsResponse:从哪里 --> 到哪里 :的具体路线信息
*/
// -1.创建起点和终点对象
// -1.1创建起点对象
MKPlacemark *startMKPlacemark = [[MKPlacemark alloc] initWithPlacemark:startCLPlacemark];
MKMapItem *startItem = [[MKMapItem alloc] initWithPlacemark:startMKPlacemark];
// -1.2创建终点对象
MKPlacemark *endMKPlacemark = [[MKPlacemark alloc] initWithPlacemark:endCLPlacemark];
MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endMKPlacemark];
// 0.创建request对象
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// 0.1设置起点
request.source = startItem;
// 0.2设置终点
request.destination = endItem;
// 1.发送请求到苹果的服务器获取导航路线信息
// 接收一个MKDirectionsRequest请求对象, 我们需要在该对象中说清楚:
// 从哪里 --> 到哪里
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
// 2.计算路线信息, 计算完成之后会调用blcok
// 在block中会传入一个响应者对象(response), 这个响应者对象中就存放着路线信息
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
// 打印获取到的路线信息
// 2.1获取所有的路线
NSArray *routes = response.routes;
for (MKRoute *route in routes) {
NSLog(@"%f千米 %f小时", route.distance / 1000, route.expectedTravelTime/ 3600);
// 3.绘制路线(本质: 往地图上添加遮盖)
// 传递当前路线的几何遮盖给地图, 地图就会根据遮盖自动绘制路线
// 当系统开始绘制路线时会调用代理方法询问当前路线的宽度/颜色等信息
[self.mapVIew addOverlay:route.polyline];
NSArray *steps = route.steps;
for (MKRouteStep *step in steps) {
// NSLog(@"%@ %f", step.instructions, step.distance);
}
}
}];
}
#pragma mark - MKMapViewDelegate
// 过时
//- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
// 绘制路线时就会调用(添加遮盖时就会调用)
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay
{
// MKOverlayRenderer *renderer = [[MKOverlayRenderer alloc] init];
// 创建一条路径遮盖
NSLog(@"%s", __func__);
#warning 注意, 创建线条时候,一定要制定几何路线
MKPolylineRenderer *line = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
line.lineWidth = 5; // 路线的宽度
line.strokeColor = [UIColor redColor];// 路线的颜色
// 返回路线
return line;
}
#pragma mark - 懒加载
- (CLGeocoder *)geocoder
{
if (!_geocoder) {
self.geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
@end
大头针的属性
#import
#import
@interface HMAnnotation : NSObject
/**
* 大头针的位置
*/
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
/**
* 大头针标题
*/
@property (nonatomic, copy) NSString *title;
/**
* 大头针的子标题
*/
@property (nonatomic, copy) NSString *subtitle;
/**
* 图标
*/
@property (nonatomic, copy) NSString *icon;
@end