Search code examples
iosswiftavfoundationlandscape-portrait

Swift - AVFoundation capture image orientation


I have a custom camera which takes photo in portrait mode.

My issue is that if i try to take a photo in landscape and save it then the picture is still saved in portrait mode.

I have this function to setup the previewLayer:

 static func setupPreviewLayer(view: UIView) {
    cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
    switch UIDevice.current.orientation {
    case .portrait:
        cameraPreviewLayer?.connection?.videoOrientation = 
       AVCaptureVideoOrientation.portrait
    case .portraitUpsideDown:
        cameraPreviewLayer?.connection?.videoOrientation = 
          AVCaptureVideoOrientation.portraitUpsideDown
    case .landscapeLeft:
        cameraPreviewLayer?.connection?.videoOrientation =
        AVCaptureVideoOrientation.landscapeLeft
    case .landscapeRight:
        cameraPreviewLayer?.connection?.videoOrientation = 
         AVCaptureVideoOrientation.landscapeRight
    default:
        cameraPreviewLayer?.connection?.videoOrientation = 
         AVCaptureVideoOrientation.portrait
    }
    cameraPreviewLayer?.frame = view.frame
    view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}

And I Call this in viewWillAppear (Which I believe is not the right way to do).

I then take the photo:

@IBAction func takePhotoBtnPressed(_ sender: Any) {
    let settings = AVCapturePhotoSettings()
    useFlash(settings: settings)
    settings.isAutoStillImageStabilizationEnabled = true
    CustomCamera.photoOutput?.capturePhoto(with: settings, delegate: self)
}

and use this extension:

extension FishCameraVC: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    if let imageData = photo.fileDataRepresentation() {
        image = UIImage(data: imageData)


          performSegue(withIdentifier: "ShowTakenPhoto", sender: nil)
        }
    }
}

Everything works fine as long as i shot a portrait mode photo but when i rotate the phone to shot a landscape photo then the the photo is still shot in portrait.

It is the first time i work with a custom camera and I really don't know where to start to fix this... If anybody could help i would really appreciate it.

Thank you!

---------------UPDATE I have added this function:

func managePhotoOrientation() -> UIImageOrientation {
    var currentDevice: UIDevice
    currentDevice = .current
    UIDevice.current.beginGeneratingDeviceOrientationNotifications()
    var deviceOrientation: UIDeviceOrientation
    deviceOrientation = currentDevice.orientation

    var imageOrientation: UIImageOrientation!

    if deviceOrientation == .portrait {
        imageOrientation = .up
        print("Device: Portrait")
    }else if (deviceOrientation == .landscapeLeft){
        imageOrientation = .left
        print("Device: LandscapeLeft")
    }else if (deviceOrientation == .landscapeRight){
        imageOrientation = .right
        CustomCamera.cameraPreviewLayer?.connection?.videoOrientation = .landscapeRight
        print("Device LandscapeRight")
    }else if (deviceOrientation == .portraitUpsideDown){
        imageOrientation = .down
        print("Device PortraitUpsideDown")
    }else{
       imageOrientation = .up
    }
    return imageOrientation
}

And changed my extension to this:

    extension FishCameraVC: AVCapturePhotoCaptureDelegate {
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if let imageData = photo.fileDataRepresentation() {
            image = UIImage(data: imageData)
            let dataProvider  = CGDataProvider(data: imageData as CFData)
            let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
            self.image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: imageOrientation!)
            performSegue(withIdentifier: "ShowTakenPhoto", sender: nil)
        }
    }
}

The image is shown in the preview in the right orientation but then when i save it it goes to portrait...


Solution

  • You need to run the following code before calling capturePhoto()

    if let photoOutputConnection = CustomCamera.photoOutput.connection(with: AVMediaType.video) {
        photoOutputConnection.videoOrientation = videoDeviceOrientation
    }
    

    You could use your managePhotoOrientation() function to get videoDeviceOrientation.