【问题标题】:AVCaptureVideoPreviewLayer landscape orientationAVCaptureVideoPreviewLayer 横向
【发布时间】:2014-02-11 01:28:25
【问题描述】:

如何让“AVCaptureVideoPreviewLayer”以横向正确显示?它在纵向上可以正常工作,但不会旋转,并且当父视图控制器处于横向时会显示旋转的相机捕获。

【问题讨论】:

    标签: ios avfoundation calayer landscape


    【解决方案1】:

    仅仅改变 AVCaptureVideoPreviewLayer 的连接方向是不够的。 这只会更新您在屏幕上看到的图像。

    但实际图像仍将保持不变。您可以在不同方向打印输出图像大小并检查结果。

    要使旋转真正起作用,您还需要更改 AVCaptureSession 连接的方向。

    func updateVideoOrientation() {
        if let previewLayerConnection = previewLayer.connection, previewLayerConnection.isVideoOrientationSupported {
            previewLayerConnection.videoOrientation = currentVideoOrientation
        }
        if let captureSessionConnection = captureSession.connections.first, captureSessionConnection.isVideoOrientationSupported {
            captureSessionConnection.videoOrientation = currentVideoOrientation
        }
    }
    

    【讨论】:

      【解决方案2】:

      Jacksanford 和 NeonEye 的回答很棒。不过,我有一个快速的建议。

      因为 statusBarOrientation 在 iOS 13 中已弃用,所以我最终使用了

      connection.videoOrientation = self.interfaceOrientation(toVideoOrientation: UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .portrait

      【讨论】:

        【解决方案3】:

        Swift 5 (iOS 13.0+):

            func updateVideoOrientation() {
                guard let videoPreviewLayer = self.videoPreviewLayer else {
                    return
                }
                guard videoPreviewLayer.connection!.isVideoOrientationSupported else {
                    print("isVideoOrientationSupported is false")
                    return
                }
                let statusBarOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
                let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation?.videoOrientation ?? .portrait
                videoPreviewLayer.frame = view.layer.bounds
                videoPreviewLayer.connection?.videoOrientation = videoOrientation
                videoPreviewLayer.removeAllAnimations()
            }
        
            override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
                super.viewWillTransition(to: size, with: coordinator)
        
                coordinator.animate(alongsideTransition: nil, completion: { [weak self] (context) in
                    DispatchQueue.main.async(execute: {
                        self?.updateVideoOrientation()
                    })
                })
            }
        
        extension UIInterfaceOrientation {
            var videoOrientation: AVCaptureVideoOrientation? {
                switch self {
                case .portraitUpsideDown: return .portraitUpsideDown
                case .landscapeRight: return .landscapeRight
                case .landscapeLeft: return .landscapeLeft
                case .portrait: return .portrait
                default: return nil
                }
            }
        }
        
        

        【讨论】:

          【解决方案4】:
          override func viewWillLayoutSubviews() {
              self.previewLayer.frame = self.view.bounds
              if previewLayer.connection.isVideoOrientationSupported {
                  self.previewLayer.connection.videoOrientation = self.interfaceOrientation(toVideoOrientation: UIApplication.shared.statusBarOrientation)
              }
          }
          
          func interfaceOrientation(toVideoOrientation orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation {
              switch orientation {
              case .portrait:
                  return .portrait
              case .portraitUpsideDown:
                  return .portraitUpsideDown
              case .landscapeLeft:
                  return .landscapeLeft
              case .landscapeRight:
                  return .landscapeRight
              default:
                  break
              }
          
              print("Warning - Didn't recognise interface orientation (\(orientation))")
              return .portrait
          }
          

          //SWIFT 3 转换

          【讨论】:

          • 这在 viewDidLoad() 没有显示和运行相机的情况下完美运行except,这会引入各种问题,因为 previewLayer 可能还不存在。因此,上面@neoneye 的回答应该被认为是正确的。
          【解决方案5】:

          Swift3

          func updateVideoOrientation() {
              guard let previewLayer = self.previewLayer else {
                  return
              }
              guard previewLayer.connection.isVideoOrientationSupported else {
                  print("isVideoOrientationSupported is false")
                  return
              }
          
              let statusBarOrientation = UIApplication.shared.statusBarOrientation
              let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation.videoOrientation ?? .portrait
          
              if previewLayer.connection.videoOrientation == videoOrientation {
                  print("no change to videoOrientation")
                  return
              }
          
              previewLayer.frame = cameraView.bounds
              previewLayer.connection.videoOrientation = videoOrientation
              previewLayer.removeAllAnimations()
          }
          
          override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
              super.viewWillTransition(to: size, with: coordinator)
          
              coordinator.animate(alongsideTransition: nil, completion: { [weak self] (context) in
                  DispatchQueue.main.async(execute: {
                      self?.updateVideoOrientation()
                  })
              })
          }
          

          extension UIInterfaceOrientation {
              var videoOrientation: AVCaptureVideoOrientation? {
                  switch self {
                  case .portraitUpsideDown: return .portraitUpsideDown
                  case .landscapeRight: return .landscapeRight
                  case .landscapeLeft: return .landscapeLeft
                  case .portrait: return .portrait
                  default: return nil
                  }
              }
          }
          

          【讨论】:

          • 完美。 谢谢!
          【解决方案6】:

          除了 viewWillLayoutSubviews(),你还必须实现 didRotateFromInterfaceOrientation()。

          这是因为如果设备从纵向模式旋转到纵向倒置模式,子视图布局将不会更新(显然是出于效率原因)。

          【讨论】:

            【解决方案7】:

            首先,答案

            - (void)viewWillLayoutSubviews {
              _captureVideoPreviewLayer.frame = self.view.bounds;
              if (_captureVideoPreviewLayer.connection.supportsVideoOrientation) {
                _captureVideoPreviewLayer.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;
            }
            

            我遇到了几篇关于这个问题的 SO 帖子,但找不到任何简单的解释,所以我想分享我自己的。

            如果您在这件事上遵循 Apple 的示例,当您将 iOS 设备旋转到横向时会遇到两个潜在问题

            1. 捕捉将显示为旋转(就像您将相机保持 90 度关闭一样)
            2. 它不会完全跨越其设定的界限

            这里的问题是“CALayer”不支持自动旋转,因此,与您添加为子视图的“UIView”不同,它不会在其父“UIView”旋转时旋转。因此,每次父视图的边界发生变化时,您都必须手动更新其框架(不是父视图的框架,因为框架在旋转后保持不变)。这是通过覆盖容器视图控制器中的“viewWillLayoutSubviews”来实现的。

            其次,您应该使用“videoOrientation”属性通知 AVFoundation 方向,以便它正确进行预览。

            希望这会有所帮助。

            【讨论】:

            • 太棒了!但是我必须通过调用 self.view.setNeedsLayout() 来实现 will- 或 didRotateToInterfaceOrientation() 来调用 viewWillLayoutSubviews
            • 这个方法对我帮助很大。谢谢队友
            • 当您多次旋转设备(尤其是 180° 旋转)时,有时使用 viewWillLayoutSubviews() 会搞砸。在 willRotateToInterfaceOrientation() 中实现相同的行为更好地解决了我的问题。无论如何谢谢;)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-25
            • 1970-01-01
            • 2018-05-20
            • 1970-01-01
            相关资源
            最近更新 更多