Search code examples
iosswiftxcode13

SwiftUI 5: How to lock orientation for a specific view


On my camera view, I want to keep the orientation as portrait because when it's in landscape, the camera preview does not rotate:


struct CameraPreview: UIViewRepresentable {
    
    @ObservedObject var camera: CameraModel
    
    func makeUIView(context: Context) -> some UIView {
        
        let view = UIView(frame: UIScreen.main.bounds)
        
        camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
        
        //...
        camera.preview.videoGravity = .resizeAspectFill
        camera.preview.frame = view.frame
        view.layer.addSublayer(camera.preview)
        
        
        // starting session
        camera.session.startRunning()
        
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
}

I know I'll need to fix this sooner or later but I need to get moving. For now, I'll prevent all other orientations except:


struct CameraView: View {
    init() {
        let value = UIInterfaceOrientation.portrait.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
    }

[..]

By doing the above, when put in landscape, the camera preview cuts in half then the init "kicks in" which sets it back CameraView back into portrait. How to let it stay in portrait mode without any animation?

Placing the code inside .onAppear does nothing:



struct CameraView: View {
  var body: some View {

    VStack{..}
      .onAppear(perform: {
        let value = UIInterfaceOrientation.portrait.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
      }
  }


Solution

  • Well it turns out I've fixed the rotation instead:

    func updateUIView(_ uiView: UIViewType, context: Context) {
        let view = UIView(frame: UIScreen.main.bounds)
        let statusBarOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
        let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation?.videoOrientation ?? .portrait
        camera.preview.frame = view.frame
        camera.preview.connection?.videoOrientation = videoOrientation
    }
    

    For videoOrientation see Jack's answer.

    
    import AVFoundation
    
    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
            }
        }
    }