我试图禁用任何可识别的方向旋转到AVCaptureVideoPreviewLayer,同时仍然保持任何子视图的旋转。AVCaptureVideoPreviewLayer确实有一个朝向属性,改变它确实允许图层在任何朝向下正确显示。然而,旋转涉及到AVCaptureVideoPreviewLayer的一些时髦的旋转,而不是像在Camera应用程序中那样保持平滑。
这就是我如何使方向正常工作,减去旋转中的障碍:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
_captureVideoPreviewLayer.frame = self.view.bounds;
_captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation];
}
我如何让这个层像相机应用程序一样,同时保持其子视图的旋转?
另外,作为旁白,我已经看到方向属性在AVCaptureVideoPreviewLayer上贬值,而AVCaptureConnection的videoOrientation属性应该被使用,但我不认为我有一个AVCaptureConnection在这里访问(我只是在屏幕上显示相机)。我应该如何设置这个访问videoOrientation?
对包含预览层的视图使用仿射变换创建平滑过渡:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (cameraPreview) {
if (toInterfaceOrientation==UIInterfaceOrientationPortrait) {
cameraPreview.transform = CGAffineTransformMakeRotation(0);
} else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
cameraPreview.transform = CGAffineTransformMakeRotation(M_PI/2);
} else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) {
cameraPreview.transform = CGAffineTransformMakeRotation(-M_PI/2);
}
cameraPreview.frame = self.view.bounds;
}
}
在旋转动画块中调用willAnimateRotationToInterfaceOrientation
函数,因此此处更改属性将与其他旋转动画一起动画。这个功能在iOS 6中已经被淘汰,取而代之的是处理设备旋转的新方法,所以这个功能目前可以工作,但不是最好的解决方案。
这是一个老问题,但由于它没有一个公认的答案,而且我自己在过去的几天里一直在处理这个问题,我想我应该把答案贴出来。
根据设备方向变化旋转视频的技巧是根本不旋转 AVCaptureVideoPreviewLayer
或AVCaptureConnection
。
改变AVCaptureConnection
的方向(AVCaptureVideoPreviewLayer
的方向已被弃用)ALWAYS会导致一个丑陋的动画变化。在视频旋转之后,你仍然需要变换预览层。
正确的方法是使用AVAssetWriter
写入视频和音频数据。然后在你开始录制的时候对AVAssetWriterInput
应用一个变换。
这是苹果公司关于这个的宣传——
从AVCaptureVideoDataOutput接收旋转CVPixelBuffers
请求缓冲区旋转,客户端调用-setVideoOrientation: onAVCaptureVideoDataOutput的视频AVCaptureConnection。请注意,物理旋转缓冲区确实会带来性能成本,所以只有如果需要,请求轮换。例如,如果您想要旋转视频写入QuickTime电影文件使用avassetwwriter,属性上设置-transform属性更可取AVAssetWriterInput而不是物理旋转缓冲区AVCaptureVideoDataOutput。
转换的应用类似于上面发布的代码。
- 你注册设备方向的改变。
- 根据新的设备方向跟踪转换应用
- 当记录被按下时,设置AVAssetWriterInput并对其应用transform。
我可以用下面的代码完成这个任务:
当你需要相机时设置:
preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain];
[self.view addSubview:preview];
videoPreviewWithFrame
函数。
- (UIView *) videoPreviewWithFrame:(CGRect) frame {
AVCaptureVideoPreviewLayer *tempPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]];
[tempPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
tempPreviewLayer.frame = frame;
UIView* tempView = [[UIView alloc] init];
[tempView.layer addSublayer:tempPreviewLayer];
tempView.frame = frame;
[tempPreviewLayer autorelease];
[tempView autorelease];
return tempView;
}
我想让AVCaptureVideoPreviewLayer漂亮地遵循所有旋转。这就是我是如何做到的(overlayView
只是一个视图,恰好有正确的界限)。只有当你将调用放置在代码中的super位置时,它才会在没有弹出的情况下产生动画:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if ([[[self previewLayer] connection] isVideoOrientationSupported])
{
[[[self previewLayer] connection] setVideoOrientation:toInterfaceOrientation];
}
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
CGRect layerRect = [[self overlayView] bounds];
[[self previewLayer] setBounds:layerRect];
[[self previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),
CGRectGetMidY(layerRect))];
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration: duration];
}
你应该使用CATransform3DMakeRotation
,这意味着图层,就像它已经在评论中提到的:
float degreesToRotate = 90.0; //Degrees you want to rotate
self.previewLayer.transform = CATransform3DMakeRotation(degreesToRotate / 180.0 * M_PI, 0.0, 0.0, 1.0);
在我的例子中,是"自我"。previewLayer:要旋转的图层。记住,你应该把它剪辑到它的容器视图的边界上:
self.previewLayer.frame = self.view.bounds;
。..
EDIT:如果你要旋转设备(willAnimateRotationToInterfaceOrientation
)的结果,你应该做这样的转换:
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
//**Perform Transformation here**
[CATransaction commit];
这样,图层会随着视图旋转
我是这样做的:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (tookFirstPicture)
return;
if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
[previewLayer setOrientation:AVCaptureVideoOrientationPortrait];
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeRight];
}
else {
[previewLayer setOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
}
}
在swift中我使用:
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
if !UIDevice.currentDevice().model.contains("Simulator") {
if (toInterfaceOrientation == .Portrait) {
prevLayer?.transform = CATransform3DMakeRotation(0, 0, 0, 1)
} else if (toInterfaceOrientation == .LandscapeLeft) {
prevLayer?.transform = CATransform3DMakeRotation(CGFloat(M_PI)/2, 0, 0, 1)
} else if (toInterfaceOrientation == .LandscapeRight) {
prevLayer?.transform = CATransform3DMakeRotation(-CGFloat(M_PI)/2, 0, 0, 1)
}
prevLayer?.frame = self.scanView.frame
}
}
在研究了苹果的例子之后,我已经找到了如何在iOS 11和更高版本中禁用videoPreviewLayer
的旋转。(不确定旧版本是否适用)
只需将clipToBounds = false
设置在videoPreviewLayer
的UIView
上。
由于AVCaptureVideoPreviewLayer
是一个cal_layer,那么任何更改将是动画,http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html。要停止动画,只需点击[previewLayer removeAllAnimations]
.