本节书摘来自异步社区《iOS 6高级开发手册(第4版)》一书中的第1章,第1.7节同步获取当前的加速计角度,作者 【美】Erica Sadun,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.7 同步获取当前的加速计角度
iOS 6高级开发手册(第4版)
有时,你可能想在不用把自己设定为完全委托的情况下查询加速计。下面的方法打算在UIDevice类别内使用,允许与x/y平面(iOS设备的正面)一起同步返回当前的设备角度。为此,可输入一个新的运行循环,等待加速计事件,从那个回调中获取当前的角度,然后让运行循环返回那个角度:
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
float xx = acceleration.x;
float yy = -acceleration.y;
device_angle = M_PI / 2.0f - atan2(yy, xx);
if (device_angle > M_PI)
device_angle -= 2 * M_PI;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (float) orientationAngle
{
// Supercede current delegate
id priorDelegate = [UIAccelerometer sharedAccelerometer].delegate;
[UIAccelerometer sharedAccelerometer].delegate = self;
// Wait for a reading
CFRunLoopRun();
// Restore delegate
[UIAccelerometer sharedAccelerometer].delegate = priorDelegate;
return device_angle;
}
这不是用于持续轮询的方法,可为此直接使用回调。但是,对于偶尔的角度查询,这些方法提供了对当前屏幕角度的简单、直接的访问。
1.7.1 通过加速计计算方向
在第一次启动应用程序时,UIDevice类不会报告正确的方向。仅当设备移到一个新位置或者UIViewController方法起作用之后,它才会更新方向。
直到用户把设备移开然后再移回正确的方向之后,才可能会把纵向启动的应用程序视作“纵向”的。在模拟器和iPhone设备上存在这种状况,并且很容易测试(由于对特性进行了更新,使之像设计的那样工作,因此关闭了针对这个问题的无线电探测器)。
对于解决办法,可以考虑像刚才所示的那样直接恢复角度方向。然后,在确定了设备的角度之后,从基于加速计的角度转换为设备方向。下面用代码说明了它的工作方式:
// Limited to the four portrait/landscape options
- (UIDeviceOrientation) acceleratorBasedOrientation
{
CGFloat baseAngle = self.orientationAngle;
if ((baseAngle > -M_PI_4) && (baseAngle < M_PI_4))
return UIDeviceOrientationPortrait;
if ((baseAngle < -M_PI_4) && (baseAngle > -3 * M_PI_4))
return UIDeviceOrientationLandscapeLeft;
if ((baseAngle > M_PI_4) && (baseAngle < 3 * M_PI_4))
return UIDeviceOrientationLandscapeRight;
return UIDeviceOrientationPortraitUpsideDown;
}
要知道的是,这个示例只考虑了x-y平面,而大多数用户界面决策都需要在这里做出。这个代码段完全忽略了z轴,这意味着最终将会得到模糊的随机结果:即正面朝上和正面朝下的方向。可以修改这段代码,以根据需要提供这种细微差别。
UIViewController类的interfaceOrientation实例方法报告了视图控制器的界面的方向。尽管这不能代替加速计读数,但是许多界面布局问题都基于底层的视图方向,而不是设备特征。
还要知道的是,尤其是在iPad上,子视图控制器使用的布局方向可能不同于设备方向。例如,嵌入式控制器可能在一个横向划分的视图控制器内展示一种纵向布局。即便如此,也可以考虑底层的界面方向是否可以满足方向检测代码。它可能比设备方向更可靠,尤其是在应用程序启动时。
1.7.2 计算相对角度
屏幕重定向支持意味着必须在象限中支持界面对于给定设备角度的关系,其中每个象限都用于一个可能的面向前方的屏幕方向。由于UIViewController可以自动旋转其屏幕上的视图,需要了解一些数学知识以解释那些重定向。
下面的方法(编写它是为了在UIDevice类别中使用)可以计算角度,以使得角度与设备方向保持同步。这将在垂直方向上产生简单的偏移,以匹配当前展示GUI的方式:
- (float) orientationAngleRelativeToOrientation:
(UIDeviceOrientation) someOrientation
{
float dOrientation = 0.0f;
switch (someOrientation)
{
case UIDeviceOrientationPortraitUpsideDown:
{dOrientation = M_PI; break;}
case UIDeviceOrientationLandscapeLeft:
{dOrientation = -(M_PI/2.0f); break;}
case UIDeviceOrientationLandscapeRight:
{dOrientation = (M_PI/2.0f); break;}
default: break;
}
float adjustedAngle =
fmod(self.orientationAngle - dOrientation, 2.0f * M_PI);
if (adjustedAngle > (M_PI + 0.01f))
adjustedAngle = (adjustedAngle - 2.0f * M_PI);
return adjustedAngle;
}
这个方法使用一个浮点模数获取实际的屏幕角度与界面方向角度偏移量之间的差值,返回非常重要的垂直角度偏移量。
注意:
{在iOS 6中,可以使用Info.plist代替shouldAutorotateToInterfaceOrientation:,以允许和禁止方向改变。}