前面我们用了陀螺仪、加速传感器作了一些好玩的效果,今天我们就用用第三个传感器--磁力计--来作一个AR的场景。说到AR这个词,请你们不要喷我哈,并无用到WWDC刚出的ARKit。并且今天这个例子重点是学习使用磁力计,本质上来说和AR关系并不大。噱头,就是个噱头。。。。html
磁力计跟前面的加速计、陀螺仪,都是用到了上次说的iOS当中的那个核心运动框架CoreMotion
, 也都用了CMMotionManager
。git
完成后的效果,能看到在视频输出的下面会有一个随着屏幕移动的天空星辰背景图,同时屏幕左上角会实时打印当前的方向信息、地理信息。bash
磁强计指的是各类用于测量磁场的仪器,也称磁力仪、高斯计。它能够感应地球的磁场,得到方向信息。网络
那显而易见,典型的应用场景就是用在电子罗盘和导航上面。多线程
以前看到过某个大神用磁力计简直玩出了花儿,隔空抓牛的感受。利用iPhone上磁力计、加速计和麦克风实现平面和三维上的磁铁追踪,并能实时的反馈在iPhone 屏幕上。框架
看上去屌炸了,有没有?宅胖还专门找到了这篇文章的报道,有兴趣的能够进去看看,里面有实现后的视频。mobile.163.com/14/1127/09/…ide
要用到磁力计,常常会听到有人说到“磁北”、“真北”这两个高频词,CoreMotion也会给咱们返回这两个数值。是什么意思呐?函数
纳尼?这是什么鬼?来来来,我们科普一下。学习
磁北 磁北是以大地磁场为基准的,经过各类传感器传送的方位都是以磁北为基准的。BUT!!!!敲黑板!!!!!磁北的具体位置是随着时间而改变的。 也就是说我们随着地球的旋转,我们除了有一年四季的变化、时间的变化,连磁场都会发生改变。嗯,是这样的。ui
真北 因为磁北是会变化的,那咱们怎么用?不可能还要计算地球自转轴、考虑时间因素吧。因此才有了真北这个概念。
真北是地球自转的地理北极,这个就是考虑到了各类因素,是一个固定的位置。因此我们电子罗盘神马的所指的北,说的是这个真北。一般状况下,方位都是须要矫正到真北的。苹果很贴心啊,真北就不用本身算了,直接也会有返回的数值。
剩下的还有磁偏角校订、网络北、网络北校订、收敛角等等学术概念。
那岂不是电子罗盘上面的北和指南针上面的北不一致啊?
问这个问题的童鞋那是至关的聪明呀,那确定是不一致的。不过偏差也是在可感官接受的范围内。在等会儿的例子里面,我们把这两个数值都打印出来,本身看看。
磁力计一样也是经过CoreMotion 这个框架来管理的,因此和前面两个传感器同样,四个标准步骤:
CoreMotion
中有2种获取数据方式,一种叫作PUSH的方式,一种叫作PULL的方式。顾名思义,PUSH就是被动的获取。设定完了以后,线程定时把获取到的数据推送回来。可想而知,对于资源的消耗是会稍微大一点的。PULL,就是要去索取。拉一下才会获取到数据。不要不给。上一次代码是Swift的,这一次我们就使用OC啦。
//PULL的方法获取数据
- (void)pullMagnetometer {
// 判断磁力计是否可用
if (self.manager.magnetometerAvailable) {
// 设置磁力计采样频率
self.manager.magnetometerUpdateInterval = 0.1;
//开始更新,后台线程开始运行。这是Pull方式。
[self.manager startMagnetometerUpdates];
NSLog(@"X = %f,Y = %f,Z = %f",self.manager.magnetometerData.magneticField.x,self.manager.magnetometerData.magneticField.y,self.manager.magnetometerData.magneticField.z);
} else {
NSLog(@"It cannot be used!");
}
}
复制代码
//PUSH的方法获取数据
- (void)pushMagnetometer {
// 判断磁力计是否可用
if (self.manager.magnetometerAvailable) {
// 设置磁力计采样频率
self.manager.magnetometerUpdateInterval = 0.1;
//Push方式获取和处理数据,这里咱们同样只是作了简单的打印。把采样的工做放在了主线程中。
[self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData * _Nullable magnetometerData, NSError * _Nullable error) {
NSLog(@"X = %f,Y = %f,Z = %f",magnetometerData.magneticField.x,magnetometerData.magneticField.y,magnetometerData.magneticField.z);
}];
} else {
NSLog(@"It cannot be used!");
}
}
复制代码
写这个案例的时候发现要实现一个比较逼真的AR,我们有好多东东都没有分享过。因此例子写完以后,写这篇文章的时候又对这个例子作了一些调整。大幅简化删减了了好多需求。可是,最后仍是使用了相机、百度地图,若是这两个都不用,那真的一点都不能算是AR了。
完成后的效果,能看到在视频输出的下面会有一个随着屏幕移动的天空星辰背景图,同时屏幕左上角会实时打印当前的方向信息、地理信息。
小案例里面的相机不用紧张,我们后面也仍是会分享的。还有一个以前说过的,多线程也记得的哈,下一个系列就来补。
这个例子里面我们用了百度地图,因此须要导入百度地图的SDK。由于我们没有分享过如何使用第三方库,能够看看这篇文章iOS·采用第三方(百度地图SDK)实现定位等功能开发
iPhone对于APP使用用户的隐私权限作了很严格的规定,每一个APP使用用户隐私以前必需要让用户知道而且赞成。大概也正是由于这点,本宅胖才这么爱iPhone吧。虽然开发的时候就面临着不少问题,但至少产品始终是站在用户的角度考虑问题的。
在Info.plist中向用户索取相机和地理位置信息的权限。
相机在这个案例里面,使用的是AVFoundation
框架。也是很心痛,这部分以前没有分享过。因此若是等不及俺的分享,能够先看看这个。Objc的第21期内容:iOS上的相机捕捉
别忘了在头文件<AVFoundation/AVFoundation.h>
,同时遵照代理协议AVCaptureVideoDataOutputSampleBufferDelegate
。
从网上找到的星空图是4000*2800的大小,要让它彻底超出屏幕。这样才能根据手机的移动进行活动。
一样的,为了可以明显的看到效果,在从陀螺仪获取到的数值以后,添加了一个放大倍数。这个小例子里面我们使用的是5。
若是陀螺仪返回的数据在某个特定小范围内,咱们就是视同只是手抖,不对图片自己进行处理。这样就看不到背景图片明显抖动的感受了。
// 作一下防抖动的处理,若是手机旋转的不太大,就不执行操做
if (fabs(gyroData.rotationRate.x) * multiplier < 0.2 && fabs(gyroData.rotationRate.y) * multiplier < 0.2) {
return ;
}
复制代码
直接修改背景图的center就行了,让原center添加上须要进行的位移量就能够实现了。 这里须要注意的是,须要对边界值进行处理。若是屏幕旋转的乱七八糟,咱们要让视频输出层下面始终有一个背景存在。
// 由于背景图的大小事屏幕宽度的三倍,高度的两倍。为了防止超出边界,进行限制
if (imageRotationX > self.view.frame.size.width * 1.5) {
imageRotationX = self.view.frame.size.width * 1.5;
}
if(imageRotationX < (- self.view.frame.size.width * 0.5)){
imageRotationX=(- self.view.frame.size.width * 0.5);
}
if (imageRotationY > self.view.frame.size.height) {
imageRotationY = self.view.frame.size.height;
}
if (imageRotationY < 0) {
imageRotationY = 0;
}
复制代码
根据百度地图SDK的文档,在用户的方向信息放生变化以后,会调用如下的方法。 这里咱们没有作任何特殊的处理,就只是简单的打印出来了磁北、真北、三轴的偏移量。
等会儿运行的时候你们就能看到以前的问题,到底磁北、真北之间相差多少。
/**
*用户方向更新后,会调用此函数
*@param userLocation 新的用户位置
*/
- (void)didUpdateUserHeading:(BMKUserLocation *)userLocation {
self.magnetometerInfo.numberOfLines = 0;
self.magnetometerInfo.text = [NSString stringWithFormat:@"磁北:%.0f,真北:%.0f \n偏移:%.0f \nx:%.1f y:%.1f z:%.1f",
userLocation.heading.magneticHeading,userLocation.heading.trueHeading,userLocation.heading.headingAccuracy,userLocation.heading.x,userLocation.heading.y,userLocation.heading.z];
}
复制代码
/**
*用户位置更新后,会调用此函数
*@param userLocation 新的用户位置
*/
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
self.physicalLocation.numberOfLines = 0;
self.physicalLocation.text = [NSString stringWithFormat:@"经度:%f \n纬度:%f \n高度:%f",userLocation.location.coordinate.longitude,userLocation.location.coordinate.latitude,userLocation.location.altitude];
}
复制代码
源代码下载地址:OC下载地址