上一篇说到如何给一个固定的位置在地图上显示,但是手机端的定位肯定不是这样的,而是需要随着我们的位置变化而定位,所以就需要到了这一篇中的一个类CLLocationManager(定位管理器),看到manager的第一感觉,会不会是个单例,但这一个类不是一个单例,但起着和单例一样作用,因为它所指向的内存就是手机内部负责定位的组件,而这个硬件组件的地址是唯一的,所以alloc init之后,所得到的地址是一样的。话不多讲,代码以及视图才是王道。
源码的GitHub:https://github.com/YRunIntoLove/LocationPractise
相比于上一篇文章中的weiboManager中请求数据的方法,昨天进行了相应的优化,优化只不过是对参数的要求更加简洁,毕竟逻辑层越简单越好,数据层的处理麻烦点不是问题,model类没有发生变化,方法修改如下:
/**
* 获取某个位置的周边微博
*
* @param access_token 用的token值
* @param myLocation 当前的位置
* @param count 获取数量
* @param b 获取的微博列表
*/
-(void)getWeiboNearByTimeLineWithToken:(NSString *)access_token WithLocation:(CLLocationCoordinate2D)myLocation Count:(NSInteger)count BlockHandle:(WBM)b
{
//创建参数字典
NSDictionary * parameterDict = @{@"access_token":access_token,@"lat":[NSString stringWithFormat:@"%lf",myLocation.latitude],@"long":[NSString stringWithFormat:@"%lf",myLocation.longitude],@"count":[NSString stringWithFormat:@"%ld",count]};
//开始请求
[self.operationManager GET:nearby_timeLine parameters:parameterDict success:^void(AFHTTPRequestOperation * operation, NSData * data) {
NSError * error;
//创建一个字典接收数据
NSDictionary * resultDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
//接收数组
NSArray * weiboArray = resultDict[@"statuses"];
//创建一个可变数组,存储最后的结果
NSMutableArray * resultArray = [NSMutableArray array];
//开始遍历,并且添加数据
for (NSDictionary * tempDict in weiboArray)
{
//创建一个微博模型
Weibo * weibo = [[Weibo alloc]init];
//开始赋值
weibo.userName = tempDict[@"user"][@"name"];
weibo.weiboText = tempDict[@"text"];
//为坐标赋值
NSString * lat = tempDict[@"geo"][@"coordinates"][0];
NSString * longc = tempDict[@"geo"][@"coordinates"][1];
CLLocationCoordinate2D temp = CLLocationCoordinate2DMake([lat doubleValue], [longc doubleValue]);
weibo.weiboLocationCoordinate2D = temp;
//NSLog(@"位置是:%g,%g",weibo.weiboLocationCoordinate2D.latitude,weibo.weiboLocationCoordinate2D.longitude);
//添加到可变数组
[resultArray addObject:weibo];
}
//回调
b([NSArray arrayWithArray:resultArray]);
} failure:^void(AFHTTPRequestOperation * operation, NSError * error) {
;
}];
}
CLLocationManager组件,在iOS8之前的用法就不再提及,因为在iOS8上突然发生了几个重大变动,所以就按照iOS8之后的方法以及属性进行说明
在用这个组件之前,必须先引入一个头文件
#import <CoreLocation/CoreLocation.h>
然后需要履行一个协议,如下
@interface ViewController ()<MKMapViewDelegate,CLLocationManagerDelegate>
随后需要在延展中添加这个属性,其实添加不添加无所谓,毕竟是单例作用,但是加上感觉用起来会好许多
//定位管理器
@property(nonatomic,strong)CLLocationManager * locationManager;
然后viewDidLoad中也没有了那么多定位的语句,看起来会简洁了许多,
- (void)viewDidLoad {
[super viewDidLoad];
//初始化地图视图
self.mapView = [[MKMapView alloc]initWithFrame:self.view.frame];
//初始化单例
self.weiboManager = [WeiBoManager shareWeiBoManager];
//初始化位置管理
self.locationManager = [[CLLocationManager alloc]init];
//设置导航栏的信息
self.navigationItem.title = @"地图";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(freshMapWithCheck)];//添加一个刷新按钮
//设置代理,不要忘记设置代理
self.mapView.delegate = self;
self.locationManager.delegate = self;
//判断当前定位的状态
[self freshMapWithCheck];
//添加视图
[self.view addSubview:self.mapView];
}
freshMapWithCheck的作用是什么呢,因为在iOS8之后,需要用户授权才能够获取数据,也表现了良好的保密性,所以定位之前是需要判断一下定位状态的,如下
/**
* 刷新按钮的回调 以及用于判断当前状态是否能够请求位置
*/
-(void)freshMapWithCheck
{
//如果定位管理器获得了用户的允许
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse)
{
//开始请求位置
[self.locationManager startUpdatingLocation];
}
else
{
//开始请求授权
[self.locationManager requestWhenInUseAuthorization];
}
}
接着实现协议的三个方法,因为三个方法足以,所以其他的方法可以通过观看开发文档更细致的了解
1、每次获取地理数据的时候,都要先判断一下用户是否允许,只有允许了,才可以获取数据,所以回调方法名字叫做didChangeAuthorizationStatus的方法,即授权状态发生变化时,如下
/**
* 定位管理器 授权状态发生变化时
*
* @param manager 定位管理器
* @param status 当前授权的状态
*/
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {//判断当前状态
case kCLAuthorizationStatusAuthorizedWhenInUse://在用时已经授权
//请求位置
[self.locationManager startUpdatingLocation];
break;
case kCLAuthorizationStatusNotDetermined://授权待定
//请求授权
[self.locationManager requestWhenInUseAuthorization];
break;
default:
break;
}
}
2、如果定义失败,那么需要一个失败时的回调,叫做DidFailWithError的方法,顾名思义,是请求失败或者发生错误时的回调,因为不是真机调试,所以用控制台打印一下
/**
* 定位失败时候的回调方法
*
* @param manager 定位管理器
* @param error 错误信息
*/
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"定位失败!");
}
3、就是比较重要的一步,即请求位置成功后的操作。请求之后首先需要获取当前位置,然后将地图锁定到当前位置上,接着请求附近的微博,显示,方法名字叫做:didUpdateLocation,方法如下
#pragma mark - CLLocationManager Delegate
/**
* 位置更新完毕之后进行的回调
*
* @param manager 定位管理者
* @param locations 存储位置的数组,最后一个为最新的定位
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//获取最新定位
CLLocation * location = locations.lastObject;
NSLog(@"获取了最新定位!");
//停止请求位置
[self.locationManager stopUpdatingLocation];
//初步定义地图显示的位置
MKCoordinateRegion lastRegion;
lastRegion.center = location.coordinate;
lastRegion.span = MKCoordinateSpanMake(0.1, 0.1);
//更新地图范围
self.mapView.region = lastRegion;
//开始请求微博
[self.weiboManager getWeiboNearByTimeLineWithToken:token WithLocation:lastRegion.center Count:3 BlockHandle:^(NSArray *array) {
//将请求的微博添加到地图上
[self.mapView addAnnotations:array];
}];
}
这样是不是就成功了呢,试一下呗,如果不进行相应的处理,一般情况如下
为什么出现这种情况呢,是因为开发文档有这么一句话
/*
* purpose
*
* Discussion:
* Allows the application to specify what location will be used for in their app. This
* will be displayed along with the standard Location permissions dialogs. This property will need to be
* set prior to calling startUpdatingLocation.
*
* Deprecated. Set the purpose string in Info.plist using key NSLocationUsageDescription.
*<span style="white-space:pre"> </span>废弃,将理由字符串设置在 info.plist中 用键值 NSLocationUsageDescription,所以以前是直接用这个属性来赋值的,在iOS6就已经被废除了
*/
@property(copy, nonatomic) NSString *purpose __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_7, __MAC_NA, __IPHONE_3_2, __IPHONE_6_0);
就是说在ios8之后,要想得到用户的授权必须要在supportise File中的info.plist,也就是配置文件中加这么一个字段,说明请求位置的原因,尽量简洁即可,如何添加?
左边的箭头不要点开,直接点+号,添加的key名字是NSLocationWhenInUseUsageDescription,后面的值就是填理由,比如我的是RunIntoLove请求定位
然后运行一下,就会出现图文框了
点击allow即表示允许,楼主点击了allow
点击允许之后,却发现控制台出现以下打印
0.0请不要说楼主是骗子,因为用的是模拟器,没有实际的定位硬件,但是可以模拟位置,有两种方法
1、第一种方法
、
点击custom Location,接着输入经纬度,楼主输入的是天安门的经纬度
点击回车,就有效果了,来看看
2、第二种方法
点击那个类似导航的箭头,比如楼主选择的是中国香港
来看下效果如何,不要忘记点击刷新啊,这次可不是自动刷新了
来到香港了,至于为什么会有导航栏,因为没有用stroyboard拖拽,所以必然在appDelegate中用代码实现了如下,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//创建窗口
UIWindow * window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.window = window;
//创建一个viewController对象
ViewController * viewController = [[ViewController alloc]init];
//创建一个导航栏
UINavigationController * navigationController = [[UINavigationController alloc]initWithRootViewController:viewController];
//window的根视图
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
这样,高大上的地图微博就基本搞定了。收工。