'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 1:[1]

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.

Solution 2:[2]

I had a similar issue and fixed it by generating a new image based on the taken photo:

UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
image.draw(in: CGRect(origin: .zero, size: image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Apparently the image metadata (which includes the orientation) produced by iOS is incomplete and some viewers (in my case Chrome and Safari browsers) fail to render the image correctly. By re-rendering, the metadata seems to be set correctly for all viewers.

Note that 1) in my app I also downscale the image (I don't think this changes the effect though) and 2) I capture the image by other means (i.e. UIImagePickerController); still, you might give my hacky fix a try.

Solution 3:[3]

Swift 4.2 latest syntax for AVFoundation capture image orientation (tested code).

UIGraphicsBeginImageContextWithOptions((image?.size)!, false, (image?.scale)!)
image?.draw(in: CGRect(origin: .zero, size: (image?.size)!))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Solution 4:[4]

Based on dr_barto source code I wrote an extension for UIImage:

extension UIImage {
    var orientationCorrectedImage: UIImage? {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        self.draw(in: CGRect(origin: .zero, size: self.size))
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return result
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 adamfowlerphoto
Solution 2 dr_barto
Solution 3
Solution 4 Ramis