1

I have a view controller, embedded in a tab bar controller, that, among other things, presents a AVCaptureVideoPreviewLayer in a UIView.

When the device is rotated, I want the view controller to rotate with it- except for the aforementioned UIView.

Unlike this related question, however, I am not just rotating/transforming my other views in the view controller. The other views need to use their configured autolayout rotation behavior.

I've tried several things, including simply setting the video orientation to portrait:

previewLayer.connection.videoOrientation = .portrait

to extracting the UIView to a separate view controller, embedding that view controller into the original view controller, and setting its autoRotation properties

override var shouldAutorotate: Bool {
    return false
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

but then I learned here that iOS only looks at the top-level view controller for those properties.

With everything I have tried, the video preview is rotating with the rest of the view controller- ending up sideways.

The only thing that works, but is hacky and sometimes causes the video preview to become misaligned, is this

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

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        if let videoPreviewLayerConnection = previewLayer.connection {
            if let newVideoOrientation = AVCaptureVideoOrientation(rawValue: UIApplication.shared.statusBarOrientation.rawValue) {
                videoPreviewLayerConnection.videoOrientation = newVideoOrientation
            }
        }
    }
}

I basically need the opposite of this question.

How can I force the video preview to not rotate but also allow the rest of the view controller to rotate normally? (Same behavior as iOS Camera app except that the other UI elements rotate normally instead of the 90° rotation transform)

willbattel
  • 1,040
  • 1
  • 10
  • 37

1 Answers1

-1

The following is possibly as hacky as your solution but it looks cleaner visually.

In viewWillTransition I set the affine transform of the previewView to counteract the orientation set by rotating the phone. It looks cleaner than just setting the videoOrientation as the affine transform animates at the same speed as the orientation change. It is done as follows.

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

    let orientation = UIDevice.current.orientation
    var rotation : CGFloat = self.previewView.transform.rotation
    switch(orientation) {
    case .portrait:
        rotation = 0.0
    case .portraitUpsideDown:
        rotation = CGFloat.pi
    case .landscapeLeft:
        rotation = -CGFloat.pi/2.0
    case .landscapeRight:
        rotation = CGFloat.pi/2.0
    default:
        break
    }
    let xScale = self.previewView.transform.xScale
    let yScale = self.previewView.transform.yScale
    self.previewView.transform = CGAffineTransform(scaleX:xScale, y:yScale).rotated(by:rotation)
    self.view.layoutIfNeeded()

}

Here is the extension to CGAffineTransform the code above uses

extension CGAffineTransform {
    public var xScale: CGFloat {
        get {return sqrt(self.a * self.a + self.c * self.c) }
    }
    public var yScale: CGFloat {
        get {return sqrt(self.b * self.b + self.d * self.d) }
    }
    public var rotation: CGFloat {
        get {return CGFloat(atan2f(Float(self.b), Float(self.a))) }
    }
}
adamfowlerphoto
  • 2,708
  • 1
  • 11
  • 24
  • I'm not sure if you're using outdated CGAffineTransform properties or if I'm missing something, but I'm getting `Value of type 'CGAffineTransform' has no member 'xScale'` and `Value of type 'CGAffineTransform' has no member 'yScale'` where you're setting the `xScale` and `yScale` constants. I'm also getting `Value of type 'CGAffineTransform' has no member 'rotation'; did you mean 'rotated'?` where you declare the `rotation` variable. I know there have been some changes to the Core Graphics library in recent Swift versions but I don't know how to implement this in Swift 4.2. – willbattel Jan 22 '19 at 19:23
  • 1
    Ah shit forgot I wrote a little extension to CGAffineTransform. I'll add it to the answer – adamfowlerphoto Jan 22 '19 at 22:51