我的应用只是风景。我将这样呈现AVCaptureVideoPreviewLayer:
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
[self.previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
NSLog(@"previewView: %@", self.previewView);
CALayer *rootLayer = [self.previewView layer];
[rootLayer setMasksToBounds:YES];
[self.previewLayer setFrame:[rootLayer bounds]];
NSLog(@"previewlayer: %f, %f, %f, %f", self.previewLayer.frame.origin.x, self.previewLayer.frame.origin.y, self.previewLayer.frame.size.width, self.previewLayer.frame.size.height);
[rootLayer addSublayer:self.previewLayer];
[session startRunning];
self.previewView的框架为(0,0,568,320),这是正确的。 self.previewLayer记录一个(0,0,568,320)的帧,这在理论上是正确的。但是,相机显示在横向屏幕中间显示为纵向矩形,并且相机预览图像的方向错误90度。我究竟做错了什么?我需要在横屏模式下以全屏模式显示相机预览图层,并且图像应正确定位。
答案 0 :(得分:64)
SWIFT 3.0和XCODE 8.0的最佳答案
private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
layer.videoOrientation = orientation
previewLayer.frame = self.view.bounds
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.previewLayer?.connection {
let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection
if previewLayerConnection.isVideoOrientationSupported {
switch (orientation) {
case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
break
case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
break
case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
break
default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
}
}
}
}
SWIFT 2.2和XCODE 7.3的最佳答案
private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
layer.videoOrientation = orientation
previewLayer.frame = self.view.bounds
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.previewLayer?.connection {
let currentDevice: UIDevice = UIDevice.currentDevice()
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.supportsVideoOrientation) {
switch (orientation) {
case .Portrait: updatePreviewLayer(previewLayerConnection, orientation: .Portrait)
break
case .LandscapeRight: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeLeft)
break
case .LandscapeLeft: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeRight)
break
case .PortraitUpsideDown: updatePreviewLayer(previewLayerConnection, orientation: .PortraitUpsideDown)
break
default: updatePreviewLayer(previewLayerConnection, orientation: .Portrait)
break
}
}
}
}
答案 1 :(得分:63)
默认的相机方向是横向左侧(左侧的主页按钮)。你需要在这里做两件事:
1-将previewLayer框架更改为:
self.previewLayer.frame=self.view.bounds;
您需要将预览图层框架设置为屏幕的边界,以便在屏幕旋转时预览图层的框架发生更改(您不能使用根视图的框架,因为它不随旋转而改变,但是根视图做)。在您的示例中,您将预览层框架设置为我看不到的previewView属性。
2-您需要通过设备的旋转来旋转预览图层连接。在viewDidAppear中添加此代码:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
//Get Preview Layer connection
AVCaptureConnection *previewLayerConnection=self.previewLayer.connection;
if ([previewLayerConnection isVideoOrientationSupported])
[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
希望这能解决它。
完全披露:这是一个简化版本,因为您不关心横向右侧还是横向左侧。
答案 2 :(得分:27)
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
if let connection = self.previewLayer?.connection {
var currentDevice: UIDevice = UIDevice.currentDevice()
var orientation: UIDeviceOrientation = currentDevice.orientation
var previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.supportsVideoOrientation) {
switch (orientation) {
case .portrait:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portrait
break
case .landscapeRight:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeRight
break
case .landscapeLeft:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
break
case .portraitUpsideDown:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
break
default:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portrait
break
}
}
}
}
答案 3 :(得分:16)
我们不能使用
UIInterfaceOrientation != AVCaptureVideoOrientation
因为-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (orientation) {
case UIInterfaceOrientationPortrait:
[_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
break;
case UIInterfaceOrientationPortraitUpsideDown:
[_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
break;
case UIInterfaceOrientationLandscapeLeft:
[_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
break;
case UIInterfaceOrientationLandscapeRight:
[_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
break;
}
}
但我们可以通过以下代码测试价值......以及这项工作。
public static void alertDialogShow(Context context, String message)
{
final AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setMessage(message);
alertDialog.setButton("OK", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
// Handle your on click action
}
});
alertDialog.show();
}
答案 4 :(得分:10)
API似乎有所改变。 videoOrientation
现在是预览图层connection
属性的属性。此外,无需使用开关。 Swift 3.0的答案:
override func viewDidLayoutSubviews() {
self.configureVideoOrientation()
}
private func configureVideoOrientation() {
if let previewLayer = self.previewLayer,
let connection = previewLayer.connection {
let orientation = UIDevice.current.orientation
if connection.isVideoOrientationSupported,
let videoOrientation = AVCaptureVideoOrientation(rawValue: orientation.rawValue) {
previewLayer.frame = self.view.bounds
previewLayer.connection?.videoOrientation = videoOrientation
}
}
}
答案 5 :(得分:7)
对于那些在全功能相机预览上苦苦挣扎的人。这是生产代码。当然,缺点是方向改变时滞后。如果有人有更好的解决方案来克服这个问题,请分享
- (void)viewDidLoad
{
[super viewDidLoad];
[self initCamera];
}
- (void)initCamera
{
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
if (captureInput) {
mSession = [[AVCaptureSession alloc] init];
[mSession addInput:captureInput];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
if ([mSession isRunning]) {
[mSession stopRunning];
[mCameraLayer removeFromSuperlayer];
[self initCamera];
[self startCamera];
}
}
- (void)startCamera
{
[mSession startRunning];
Settings::getInstance()->setClearColor(Color(0, 0, 0, 0));
mCameraLayer = [AVCaptureVideoPreviewLayer layerWithSession: mSession];
[self updateCameraLayer];
[mCameraView.layer addSublayer:mCameraLayer];
}
- (void)stopCamera
{
[mSession stopRunning];
[mCameraLayer removeFromSuperlayer];
Settings::getInstance()->setDefClearColor();
}
- (void)toggleCamera
{
mSession.isRunning ? [self stopCamera] : [self startCamera];
[mGLKView setNeedsDisplay];
}
- (void)updateCameraLayer
{
mCameraLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
mCameraLayer.frame = mCameraView.bounds;
float x = mCameraView.frame.origin.x;
float y = mCameraView.frame.origin.y;
float w = mCameraView.frame.size.width;
float h = mCameraView.frame.size.height;
CATransform3D transform = CATransform3DIdentity;
if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) {
mCameraLayer.frame = CGRectMake(x, y, h, w);
transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0);
transform = CATransform3DRotate(transform, -M_PI/2, 0, 0, 1);
} else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation]) {
mCameraLayer.frame = CGRectMake(x, y, h, w);
transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0);
transform = CATransform3DRotate(transform, M_PI/2, 0, 0, 1);
} else if (UIDeviceOrientationPortraitUpsideDown == [[UIDevice currentDevice] orientation]) {
mCameraLayer.frame = mCameraView.bounds;
transform = CATransform3DMakeRotation(M_PI, 0, 0, 1);
} else {
mCameraLayer.frame = mCameraView.bounds;
}
mCameraLayer.transform = transform;
}
enter code here
答案 6 :(得分:4)
由于使用上述解决方案有弃用和转换警告,并且在iOS7中设置videoOrientation似乎不起作用,我在AVCaptureVideoPreviewLayer的getter中检查方向如下:
- (AVCaptureVideoPreviewLayer *) previewLayer
{
if(!_previewLayer)
{
_previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession: self.captureSession];
[_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
_previewLayer.frame = self.view.bounds; // Assume you want the preview layer to fill the view.
[_previewLayer setPosition:CGPointMake(0,0)];
if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) {
_previewLayer.transform = CATransform3DMakeRotation(-M_PI/2, 0, 0, 1);
}
else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation])
{
_previewLayer.transform = CATransform3DMakeRotation(M_PI/2, 0, 0, 1);
}
}
return _previewLayer;
}
答案 7 :(得分:3)
适用于Swift 4,Xcode 9:
override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransition(to: size, with: coordinator)
guard
let conn = self.previewLayer?.connection,
conn.isVideoOrientationSupported
else { return }
let deviceOrientation = UIDevice.current.orientation
switch deviceOrientation {
case .portrait: conn.videoOrientation = .portrait
case .landscapeRight: conn.videoOrientation = .landscapeLeft
case .landscapeLeft: conn.videoOrientation = .landscapeRight
case .portraitUpsideDown: conn.videoOrientation = .portraitUpsideDown
default: conn.videoOrientation = .portrait
}
}
这里需要注意的一点是,UIDeviceOrientation.landscapeRight
与AVCaptureVideoOrientation.landscapeLeft
一致。
另一个景观案例同样不匹配。这是故意的,并且容纳了UIKit和AVFoundation之间不幸的不匹配。如果您通过匹配名称来匹配案例,则它将无效,并且您的视频将在横向配置中颠倒。
答案 8 :(得分:3)
从UIDeviceOrientation
到AVCaptureVideoOrientation
的正确映射是必要的。
如果您的应用支持device rotation
,则还必须使用resizing preview.frame
,并且应从func
和viewDidLayoutSubviews()
调用此viewWillTransition()
。
private func configureVideoOrientation() {
if let preview = self.previewLayer,
let connection = preview.connection {
let orientation = UIDevice.current.orientation
if connection.isVideoOrientationSupported {
var videoOrientation: AVCaptureVideoOrientation
switch orientation {
case .portrait:
videoOrientation = .portrait
case .portraitUpsideDown:
videoOrientation = .portraitUpsideDown
case .landscapeLeft:
videoOrientation = .landscapeRight
case .landscapeRight:
videoOrientation = .landscapeLeft
default:
videoOrientation = .portrait
}
preview.frame = self.view.bounds
connection.videoOrientation = videoOrientation
}
}
}
答案 9 :(得分:1)
首先我们需要创建AVCaptureVideoPreviewLayer并:
设置框架。
[_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[_videoPreviewLayer setFrame:_viewPreview.layer.bounds];
最初设置方向
if (_videoPreviewLayer.connection.supportsVideoOrientation) {
_videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
使用简单的开关盒设置每个盒子的方向
-(AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:
(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft ;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
break;
}
NSLog(@"Warning - Didn't recognise interface orientation (%d)",orientation);
return AVCaptureVideoOrientationPortrait;
}
(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return AVCaptureVideoOrientationPortrait;
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return AVCaptureVideoOrientationLandscapeLeft ;
case UIInterfaceOrientationLandscapeRight:
return AVCaptureVideoOrientationLandscapeRight;
default:
break;
}
NSLog(@"Warning - Didn't recognise interface orientation (%d)",orientation);
return AVCaptureVideoOrientationPortrait;
答案 10 :(得分:1)
我也面临同样的问题 这是为了修复我的相机方向
override func shouldAutorotate() -> Bool {
return false
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.LandscapeLeft
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.LandscapeLeft
}
修理相机
let previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.avCaptureSession)
previewLayer.frame = self.view.layer.frame
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
答案 11 :(得分:1)
Swift 5版本
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updatePreview()
}
func updatePreview() {
let orientation: AVCaptureVideoOrientation
switch UIDevice.current.orientation {
case .portrait:
orientation = .portrait
case .landscapeRight:
orientation = .landscapeLeft
case .landscapeLeft:
orientation = .landscapeRight
case .portraitUpsideDown:
orientation = .portraitUpsideDown
default:
orientation = .portrait
}
if previewLayer?.connection?.isVideoOrientationSupported == true {
previewLayer?.connection?.videoOrientation = orientation
}
previewLayer.frame = view.bounds
}
答案 12 :(得分:1)
Maselko's answer对我几乎有用,但是如果状态栏方向发生翻转,则相机输出将倒置显示。我已经通过在状态栏翻转时重新调用Maselko的逻辑解决了该问题。
这是我修改的Maselko解决方案(在ios12 / swift4上测试):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setCameraOrientation()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
setCameraOrientation()
}
@objc func setCameraOrientation() {
if let connection = self.previewLayer?.connection {
let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection
if previewLayerConnection.isVideoOrientationSupported {
let o: AVCaptureVideoOrientation
switch (orientation) {
case .portrait: o = .portrait
case .landscapeRight: o = .landscapeLeft
case .landscapeLeft: o = .landscapeRight
case .portraitUpsideDown: o = .portraitUpsideDown
default: o = .portrait
}
previewLayerConnection.videoOrientation = o
previewLayer!.frame = self.view.bounds
}
}
}
答案 13 :(得分:1)
选定的答案适用于Swift 4.2-Xcode 10.0-iOS 12.0:
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let previewLayerConnection = self.videoPreviewLayer?.connection, previewLayerConnection.isVideoOrientationSupported {
updatePreviewLayer(layer: previewLayerConnection, orientation: UIApplication.shared.statusBarOrientation.videoOrientation)
}
}
private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
layer.videoOrientation = orientation
videoPreviewLayer?.frame = self.view.bounds
}
不要忘记从UIInterfaceOrientation到AVCaptureVideoOrientation的映射
extension UIInterfaceOrientation {
public var videoOrientation: AVCaptureVideoOrientation {
switch self {
case .portrait:
return AVCaptureVideoOrientation.portrait
case .landscapeRight:
return AVCaptureVideoOrientation.landscapeRight
case .landscapeLeft:
return AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
return AVCaptureVideoOrientation.portraitUpsideDown
default:
return AVCaptureVideoOrientation.portrait
}
}
}
答案 14 :(得分:0)
Maselko答案的改进版本
效果很好!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = previewView.connection {
if connection.isVideoOrientationSupported {
let videoOrientation = AVCaptureVideoOrientation.init(rawValue: UIApplication.shared.statusBarOrientation.rawValue)!
connection.videoOrientation = videoOrientation
previewView.frame = self.view.bounds
}
}
}
答案 15 :(得分:0)
这是我在Swift 4中使用的解决方案。
它很短,对我来说很漂亮。
=True
答案 16 :(得分:0)
@Maselko的答案是正确的,但有一个原因:您应该使用var newFragmentTag = typeof(NewFragment).Name;
var newFragment = FragmentManager.FindFragmentByTag(newFragmentTag);
if(newFragment != null)
{
FragmentManager.BeginTransaction().Hide(currentFragment).Show(newFragment).Commit();
}
else
{
// new Instance of newFragment and hide/show ...
}
而不是UIApplication.shared.statusBarOrientation
,因为设备方向是物理放置设备的方式。当您的设备处于横向状态时,它会中断,但您的UI不支持该方向(例如当我制作仅横向模式的摄像头应用程序并在设备处于纵向位置时启动视图时)。
UIDevice.current.orientation
答案 17 :(得分:0)
它在iOS 8到11.1中对我没有任何问题的唯一方法是这样做,我应该提到在我的情况下,我只在横向模式下加载我的应用程序,但它应该在所有方向上工作。(顺便说一下)可以通过imageview或任何你想要的方式手动覆盖相机)
@interface ViewController (){
AVCaptureVideoPreviewLayer * previewLayer;
AVCaptureSession* session;
}
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewView;
-(void)viewDidLoad{
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
if (captureInput) {
session = [[AVCaptureSession alloc] init];
[session addInput:captureInput];
}
previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
[previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
[previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
CALayer *rootLayer = [self.cameraPreviewView layer];
[rootLayer setMasksToBounds:YES];
[previewLayer setFrame:[self.view bounds]];
[rootLayer addSublayer:previewLayer];
[session startRunning];
//Orientation Code is in viewDidLayoutSubviews method
}
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (orientation) {
case UIInterfaceOrientationPortrait:
[previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
break;
case UIInterfaceOrientationPortraitUpsideDown:
[previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
break;
case UIInterfaceOrientationLandscapeLeft:
[previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
break;
case UIInterfaceOrientationLandscapeRight:
[previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
break;
default:break;
}
}