根据我对您问题的理解(与其他回答有所不同),以下是部分答案。
您已将应用程序锁定为纵向方向。因此,状态栏始终位于手机的纵向顶部,而不管手机的方向如何。这成功地锁定了您的界面,包括您的AVCapture接口。但是,您还希望锁定从相机获取的原始图像数据流,以便图像水平线始终平行于状态栏。
这最好需要 连续地 进行 - 这样,如果您将相机放在45度角,图像将被逆时针旋转45度。否则,大多数情况下,图像将无法正确对齐(另一种选择是图像始终不对齐,直到您90度的方向切换更新,这将使图像旋转90度)。
为此,您需要使用Core Motion和加速度计。您想要获取手机Y轴相对于真正垂直的角度,并相应地旋转图像。有关几何详细信息,请参见此处:
iPhone orientation -- how do I figure out which way is up?
使用Core Motion,从viewDidLoad触发此方法。
- (void)startAccelerometerUpdates {
self.coreMotionManager = [[CMMotionManager alloc] init];
if ([self.coreMotionManager isAccelerometerAvailable] == YES) {
CGFloat updateInterval = 0.1;
[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
已经修复此问题)。
现在方向已经相当正确,但是您的图像不适合您的视图。这样做的原因是相机原始数据的格式由其传感器阵列的物理尺寸确定。解决方法是缩放图像,使您在所有角度都有足够的多余图像数据,以使您能够裁剪图像以适合所需的竖屏格式。
在界面构建器中:
- 将预览图层的视图设置为正方形,居中于其父视图,宽度和高度等于可见图像区域的对角线(sqrt(width2+height2))
或者在代码中:
- (void)resizeCameraView
{
CGSize size = self. videoPreviewView.bounds.size;
CGFloat diagonal = sqrt(pow(size.width,2)+pow(size.height,2));
diagonal = 2*ceil(diagonal/2);
self.videoPreviewView.bounds = (CGRect){0,0,diagonal,diagonal};
}
如果您在代码中这样做,
resizeCameraView
应该可以在
viewDidLoad
中调用。确保
self.videoPreviewView
是正确视图的IBOutlet引用。
现在当您拍照时,您将捕获来自相机阵列的整个“原始”图像数据,它将以横向格式保存。它将保存一个方向标志以进行显示旋转。但是,您可能希望将照片保存为屏幕上看到的内容。这意味着您需要旋转和裁剪照片以匹配您的屏幕视图,然后删除其方向元数据。这就是要解决的问题('partial answer'的另一部分):我怀疑您可能会发现这种方法并不能完全满足您的需求(我认为您真正想要的是一个相机传感器,可以针对设备旋转而旋转以保持地平线稳定)。
更新更改
startAccelerometerUpdates
,从
acos
获取角度改为从
atan2
获取,更平滑,考虑了所有方向而无需微调
更新2根据您的评论,似乎您旋转的预览层卡住了?我无法复制您的错误,这一定是代码或设置中的其他地方。为了让您可以检查干净的代码,我已将我的解决方案添加到
Apple的AVCam项目中,因此您可以与其进行比较。以下是要做的内容:
在AVCamViewController.m中:
#import <CoreMotion/CoreMotion.h>
- 添加我的
startAccelerometerUpdates
方法 - 添加我的
resizeCameraView
方法(将这两个方法都放在类文件的顶部,否则您可能会感到困惑,因为该文件中有多个@implementation
) - 在
viewDidLoad
中添加: [self resizeCameraView];
(它可以是该方法的第一行) - 在@interface中添加属性
@property (strong, nonatomic) CMMotionManager* coreMotionManager
(它不需要是属性,但是我的方法假设它存在,因此如果您不添加它,则必须修改我的方法)。
在
startAccelerometerUpdates
中更改此行:
self.previewLayer.transform = rotate
To:
self.captureVideoPreviewLayer.transform = rotate
此外,在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
所有结果都是流畅和准确的。