这是基于我对您的问题的理解的部分答案(与您的其他答案不同)。
您已将应用程序锁定为纵向。因此,无论手机的方向如何,状态栏始终位于手机的纵向顶部。这成功地锁定了您的界面,包括您的 AVCapture 界面。但是您还想锁定来自相机的原始图像馈送,以便图像水平始终与状态栏平行。
理想情况下,这需要连续进行- 如果您的相机处于 45 度角,则图像将反向旋转 45 度。否则,大多数情况下,图像将无法正确对齐(另一种方法是在您的 90 度方向开关更新之前它总是不对齐,这会将图像旋转 90 度)。
为此,您需要使用 Core Motion 和加速度计。您希望将手机 Y 轴的角度设为真正垂直并相应地旋转图像。有关几何细节,请参见此处:
iPhone 方向 - 我如何确定哪个方向是向上的?
使用 Core Motion,从 viewDidLoad 触发此方法
- (void)startAccelerometerUpdates {
self.coreMotionManager = [[CMMotionManager alloc] init];
if ([self.coreMotionManager isAccelerometerAvailable] == YES) {
CGFloat updateInterval = 0.1;
// Assign the update interval to the motion manager
[self.coreMotionManager setAccelerometerUpdateInterval:updateInterval];
[self.coreMotionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler: ^(CMAccelerometerData *accelerometerData, NSError *error) {
CGFloat angle = -atan2( accelerometerData.acceleration.x,
accelerometerData.acceleration.y)
+ M_PI ;
CATransform3D rotate = CATransform3DMakeRotation(angle, 0, 0, 1);
self.previewLayer.transform = rotate;
}];
}
}
a b c
电话持有 (a) 肖像;(b) 旋转约 30 度;(c) 风景
。
您可能会发现这有点跳动,并且设备移动和视图之间有一点延迟。您可以使用 updateInterval,并更深入地使用其他 Core Motion 技巧来抑制运动。(我没有处理手机完全倒置的情况,如果您将相机面朝下或面朝上,结果是不确定 的,已更新代码/使用atan2
)。
现在方向相当正确,但您的图像不适合您的视图。您对此无能为力,因为原始相机馈送的格式由其传感器阵列的物理尺寸固定。解决方法是缩放图像,以便您在各个角度都有足够的多余图像数据,以便您可以裁剪图像以适合您想要的纵向格式。
在界面生成器中:
- 将您的 previewLayer 的视图设置为以它的超级视图为中心的正方形,宽度和高度等于可见图像区域的对角线(sqrt (width 2 +height 2 )
或在代码中:
- (void)resizeCameraView
{
CGSize size = self. videoPreviewView.bounds.size;
CGFloat diagonal = sqrt(pow(size.width,2)+pow(size.height,2));
diagonal = 2*ceil(diagonal/2); //rounding
self.videoPreviewView.bounds = (CGRect){0,0,diagonal,diagonal};
}
如果您在代码中执行此操作,resizeCameraView
那么如果您从viewDidLoad
. 确保这self.videoPreviewView
是您对正确视图的 IBOutlet 引用。
现在,当您拍照时,您将从相机阵列中捕获整个“原始”图像数据,这些数据将以横向格式显示。它将与显示旋转的方向标志一起保存。但您可能想要的是保存屏幕上看到的照片。这意味着您必须在保存照片之前旋转和裁剪照片以匹配您的屏幕视图,并删除它的方向元数据。那是你要解决的问题(“部分答案”的另一部分):我怀疑你可能会认为整个方法不能让你得到你想要的(我认为你真正想要的是一个摄像头传感器- 逆着设备的旋转旋转以保持地平线稳定)。
更新
更改为从而不是startAccelerometerUpdates
获取角度,更平滑并考虑所有方向而无需摆弄atan2
acos
更新 2
从您的评论来看,您的旋转预览层似乎卡住了?我无法复制您的错误,它必须在您的代码或设置中的其他位置。
为了让您可以使用干净的代码进行检查,我已将我的解决方案添加到Apple 的 AVCam 项目中,因此您可以对其进行检查。这是做什么:
- 将 Core Motion 框架添加到 AVCam。
在 AVCamViewController.m
#import <CoreMotion/CoreMotion.h>
- 添加我的
startAccelerometerUpdates
方法
- 添加我的
resizeCameraView
方法(将这两个方法都放在类文件的顶部附近,否则您可能会感到困惑,该文件中有多个@implementation
s)
- 添加行:
[self resizeCameraView];
to viewDidLoad
(可以是方法的第一行)
- 将属性添加
@property (strong, nonatomic) CMMotionManager* coreMotionManager
到@interface(它不需要是属性,但我的方法假定它存在,所以如果你不添加它,你将不得不修改我的方法)。
在startAccelerometerUpdates
更改此行:
self.previewLayer.transform = rotate;
到:
self.captureVideoPreviewLayer.transform = rotate;
此外,在 中的 Objects 列表中AVCamViewController.xib
,将 videoPreview 视图移到 ToolBar 上方(否则,当您放大它时,您会覆盖控件)
请务必禁用旋转 - 对于 iOS<6.0,这已经是正确的,但对于 6.0+,您需要在目标摘要中仅选择支持方向的纵向。
我认为这是我对 AVCam 所做更改的完整列表,并且旋转/方向都运行良好。我建议你尝试做同样的事情。如果你能让它顺利工作,你就知道代码中的某个地方还有其他故障。如果你仍然发现你的旋转棒,我很想知道更多关于你的硬件和软件环境的信息,比如你正在测试哪些设备。
我正在 XCode 4.6/OSX10.8.2 上编译,并测试:
- iPhone4S / iOS5.1
- iPhone3G / iOS6.1
- iPad mini / iOS6.1
所有结果都是平滑和准确的。