防止AVCaptureVideoPreviewLayer旋转,但允许UI图层随方向旋转

6

我有两个视图控制器。一个是根视图控制器,包含用户界面接口,例如记录按钮。在这个视图控制器上,我还在索引0处显示另一个视图控制器的视图。该视图包含一个AVCaptureVideoPreviewLayer。

我想让我的视频相机模仿苹果视频相机应用程序,其中接口布局随着旋转而调整,但视频预览层不会调整。您可以看到股票视频应用程序中的录制计时器(UILabel)根据方向消失和出现在顶部。

有什么建议吗?我找到了一条建议,建议将预览添加到应用程序委托的窗口中,因为它不会符合导航控制器的旋转,但对我没有起作用。

谢谢!

4个回答

11

我有一个非常相似的情况。我只有一个视图控制器,我希望在其中有一个不旋转的AVCaptureVideoPreviewLayer。我发现@SeanLintern88提出的解决方案对我没有用,状态栏从未移动,我在屏幕上放置的WKWebView没有得到适当的调整大小。

我遇到的一个更大的问题之一是将我的AVCaptureVideoPreviewLayer放在视图控制器的视图中。更好的做法是创建一个新的UIView来保存这个层。

之后,我在苹果的技术说明书QA1890: Preventing a View From Rotating中找到了答案。这使我能够生成以下Swift代码:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    coordinator.animateAlongsideTransition(
        { (UIViewControllerTransitionCoordinatorContext) in
            let deltaTransform = coordinator.targetTransform()
            let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))
            var currentRotation : Float = (self.previewView!.layer.valueForKeyPath("transform.rotation.z")?.floatValue)!
            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            currentRotation += -1 * deltaAngle + 0.0001;
            self.previewView!.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")
            self.previewView!.layer.frame = self.view.bounds
        },
        completion:
        { (UIViewControllerTransitionCoordinatorContext) in
            // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
            var currentTransform : CGAffineTransform = self.previewView!.transform
            currentTransform.a = round(currentTransform.a)
            currentTransform.b = round(currentTransform.b)
            currentTransform.c = round(currentTransform.c)
            currentTransform.d = round(currentTransform.d)
            self.previewView!.transform = currentTransform
        })
}

原始技术说明没有这一行代码 self.previewView!.layer.frame = self.view.bounds,但我发现它非常必要,因为虽然锚点不会移动,但框架会。如果没有该行代码,预览将被偏移。

此外,由于我正在尽所有的努力保持视图在正确的位置,因此我不得不删除所有定位约束。当我加入了它们时,它们会导致预览相反方向偏移。


如果您使用CGAffineTransform(rotationAngle: currentRotation - deltaAngle)并将其设置在具有捕获层的视图的变换属性上(我使用了带有layerClass覆盖的UIView子类),然后更新视图框架,似乎不需要0.0001角度偏移。然后可以删除完成闭包。 - Bjørn Olav Ruud

6

请务必将 shouldAutorotate 设置为返回 false:

-(BOOL)shouldAutorotate{
    return NO;
}

注册通知以便检测方向变化:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(orientationChanged:)
                                             name:UIDeviceOrientationDidChangeNotification
                                           object:nil];

实现通知更改

-(void)orientationChanged:(NSNotification *)notif {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];

// Calculate rotation angle
CGFloat angle;
switch (deviceOrientation) {
    case UIDeviceOrientationPortraitUpsideDown:
        angle = M_PI;
        break;
    case UIDeviceOrientationLandscapeLeft:
        angle = M_PI_2;
        break;
    case UIDeviceOrientationLandscapeRight:
        angle = - M_PI_2;
        break;
    default:
        angle = 0;
        break;
}


}

并旋转用户界面

 [UIView animateWithDuration:.3 animations:^{
        self.closeButton.transform = CGAffineTransformMakeRotation(angle);
        self.gridButton.transform = CGAffineTransformMakeRotation(angle);
        self.flashButton.transform = CGAffineTransformMakeRotation(angle);
    } completion:^(BOOL finished) {

}];

这是我实现屏幕锁定但旋转UI的方法,如果这有效,请链接帖子并将其复制到那里,然后您可以标记它。 :P

1
您会发现相机应用程序仅支持纵向方向,并根据需要旋转视图元素。
我遇到了类似的问题,但最终需要AVCaptureVideoPreviewLayer保持与其余视图一起定向,以便我可以正确使用metadataOutputRectConverted(fromLayerRect:)方法。
与Erik Allens上面的答案一样,我的解决方案基于Technical Q&A QA1890 Preventing a View From Rotating,但已更新为Swift 5并在界面转换完成后删除旋转变换。
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        self.cameraPreviewView = UIView(frame: view.bounds)
        self.cameraPreviewView.layer.addSublayer(self.videoPreviewLayer)
        self.videoPreviewLayer.connection?.videoOrientation = UIApplication.shared.statusBarOrientation.asAVCaptureVideoOrientation()
        self.videoPreviewLayer.frame = self.cameraPreviewView.bounds
        self.view.addSubview(self.cameraPreviewView)
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        self.cameraPreviewView.center = self.view.bounds.midPoint
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        let cameraPreviewTransform = self.cameraPreviewView.transform

        coordinator.animate(alongsideTransition: { context in
            // Keep the camera preview view inversely rotatated with the rest of the view during transition animations
            let deltaTransform = coordinator.targetTransform
            let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)

            var previewCurrentRotation = atan2(cameraPreviewTransform.b, cameraPreviewTransform.a)

            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction,
            // preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            previewCurrentRotation += -1 * deltaAngle + 0.0001
            self.cameraPreviewView.layer.setValue(previewCurrentRotation, forKeyPath: "transform.rotation.z")
        }, completion: { context in
            // Now the view transition animations are complete, we will adjust videoPreviewLayer properties to fit the current orientation
            // Changing the frame of a videoPreviewLayer animates the resizing of the preview view, so we disable animations (actions) to remove this effect
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            self.cameraPreviewView.transform = cameraPreviewTransform
            self.cameraPreviewView.frame = self.view.bounds

            self.videoPreviewLayer.connection?.videoOrientation = UIApplication.shared.statusBarOrientation.asAVCaptureVideoOrientation()
            self.videoPreviewLayer.frame = self.cameraPreviewView.bounds

            CATransaction.commit()
        })
    }

extension UIInterfaceOrientation {
    func asAVCaptureVideoOrientation() -> AVCaptureVideoOrientation {
        switch self {
        case .portrait:
            return .portrait
        case .landscapeLeft:
            return .landscapeLeft
        case .landscapeRight:
            return .landscapeRight
        case .portraitUpsideDown:
            return .portraitUpsideDown
        default:
            return .portrait
        }
    }
}

0
在显示相机输出的视图上添加:

AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer;
if ([layer.connection isVideoOrientationSupported]) {
    [layer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
  }

AVCaptureVideoOrientationPortrait只是其中一个选项。您可以从以下选项中选择:

typedef NS_ENUM(NSInteger, AVCaptureVideoOrientation) {
    AVCaptureVideoOrientationPortrait           = 1,
    AVCaptureVideoOrientationPortraitUpsideDown = 2,
    AVCaptureVideoOrientationLandscapeRight     = 3,
    AVCaptureVideoOrientationLandscapeLeft      = 4,
}

这必须在您设置会话之后完成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接