68

I am developing the front facing camera app in iPad2 by using the UIImagePickerController.

When I capture the image it's shows as flipped from left to right.

How do I correct this?

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) 
    {
        UIImagePickerController *imgPkr = [[UIImagePickerController alloc] init];
        imgPkr.delegate = self;
        imgPkr.sourceType = UIImagePickerControllerSourceTypeCamera;
        imgPkr.cameraDevice=UIImagePickerControllerCameraDeviceFront;


        UIImageView *anImageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"select%d.png",val]]];
        anImageView.frame = CGRectMake(0, 0, anImageView.image.size.width, anImageView.image.size.height);
        imgPkr.cameraOverlayView = anImageView;
        [theApp.TabViewControllerObject presentModalViewController:imgPkr animated:YES];
        [imgPkr release];
    }
1
  • NOTE that in iPhone - most versions - you seem to automatically get the flash and front-rear buttons. (Might depend slightly on the iOS sub-version.) It's only on iPad where you seem to need to manually add it. I just wasted time carefully developing a button, as I was testing on an iPadMini rather than an iPhone :) Commented Dec 3, 2013 at 15:15

11 Answers 11

76

You can flip the image from the source image use this

UIImage *flippedImage = [UIImage imageWithCGImage:picture.CGImage scale:picture.scale orientation:UIImageOrientationLeftMirrored];

Edit: Added swift code

let flippedImage = UIImage(CGImage: picture.CGImage, scale: picture.scale, orientation:.LeftMirrored)
Sign up to request clarification or add additional context in comments.

1 Comment

it might be downvoted because after such orientation change all the parameters are messed up and when you upload this image to a database, you can't fix the orientation (portrait becomes sideways).
19

I had the same problem - and the solution above only got me half the answer, because the user had to approve the mirrored image before getting to the next page of my app - where I use the captured image after flipping it.

To solve this I had to flip the camera view whenever I switch to the front facing camera:

- (IBAction)flipCamera:(id)sender {
if(cameraUI.cameraDevice == UIImagePickerControllerCameraDeviceFront)
{
    cameraUI.cameraDevice = UIImagePickerControllerCameraDeviceRear;
}
else {
    cameraUI.cameraDevice = UIImagePickerControllerCameraDeviceFront;
}
cameraUI.cameraViewTransform = CGAffineTransformScale(cameraUI.cameraViewTransform, -1,     1);     
}

Just to expand on this great answer, some typical complete code, Dec2013, iOS7 / Xcode5. Does everything. You just need an icon (cameraToggle.PNG in the example).

-(void)showTheDeviceCamera
    {
    if ( ! [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] )
        return;

    // self.cameraController is a UIImagePickerController
    self.cameraController = [[UIImagePickerController alloc] init];
    self.cameraController.delegate = (id)self;
    self.cameraController.mediaTypes = @[(NSString *)kUTTypeImage];
    self.cameraController.allowsEditing = YES;
    self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
    [self presentViewController:self.cameraController animated:YES completion:NULL];


        // Add front-rear toggle button MANUALLY, IF NECESSARY
        // (You seem to usually get it for free, on iPhone, but
        // need to add manually on an iPad.)

        UIView *buttonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cameraToggle"]];
        [buttonView sizeToFit];

        buttonView.userInteractionEnabled = YES;
        [self.cameraController.view addSubview:buttonView];

        UITapGestureRecognizer *tap =
            [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_frontRearButtonClicked) ];
        tap.numberOfTapsRequired = 1;
        [buttonView addGestureRecognizer:tap];

        // we'll add it at the top right .. could be anywhere you want
        buttonView.center = CGPointMake(
                self.cameraController.view.frame.size.width-buttonView.frame.size.width,
                3.0 * buttonView.frame.size.height
                );

    }

-(void)_frontRearButtonClicked
    {
    [UIView transitionWithView:self.cameraController.view
        duration:1.0
        options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionTransitionFlipFromLeft
        animations:^{
            if ( self.cameraController.cameraDevice == UIImagePickerControllerCameraDeviceRear )
                self.cameraController.cameraDevice = UIImagePickerControllerCameraDeviceFront;
            else
                self.cameraController.cameraDevice = UIImagePickerControllerCameraDeviceRear;
        } completion:NULL];
    }

3 Comments

This is the most useful answer.
How did you set up target-action for the flip camera? Do you somehow have the camera UI in your storyboard?
This view transform works great if your users aren't planning on rotating their phone to take a landscape shot with the front camera. :)
11

As the other answers, I had the same problem. As Yonatan Betzer mentioned, just flip the final image is only half the answer, because the preview image, displayed by the UIPickerController when you take a picture with the front camera, it's still inverted (mirrored).

Yonatan Betzer's anwser works great, but he did not mentioned how or where to put the action to change the camera device.

Based in some codes from internet, I created a Pod to get this wanted behavior:

https://github.com/lucasecf/LEMirroredImagePicker

After installed, you just have to call this two lines of code together with your UIImagePickerController:

self.mirrorFrontPicker = [[LEMirroredImagePicker alloc] initWithImagePicker:pickerController];
[self.mirrorFrontPicker mirrorFrontCamera];

And thats it, simply as that. You can check for more informations in the README of the github link.

5 Comments

I got the next error when using your classes at class LEImagePickerController.m: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Source type must be UIImagePickerControllerSourceTypeCamera' any idea why?
Hi @FernandoSantiago, you were executing the classes in the simulator, right? This project was assuming that a camera was avaliable in the used device, unfortunately the simulator cannot simulate the camera, and that's why it was crashing. I already commited, today, a little workaround to treat this case, showing the photo library if no camera is avaliable, but you will only be able to see the full functionality of the project in the device, with a real camera, and not in the simulator.
@LucasEduardo amigo this is not working... the "preview" image (where you press "use" is not mirrored correctly)
@B-Man I just tested with iphone6/iOS9/Xcode 7 and the lib working how is supposed to. Try do download the zip from github and run the example project in your device. If you are still having any trouble, please create a issue in github providing more details.
@LucasEduardo I should have elaborated more. In your example app, you flip the "live camera" feed. That is when I turn my head left, the mirrored live view turns my head right. From there, the image in the "use image" view and output image are as expected. But i do not want to see my head turned left when I turn my head right. Try Snapchat, what you see is what you get. I think that is the expected behaviour. Of course you could implement your own but still :)
6

Just to add how I have just achieved this without subclassing UIImagePickerController and without adding extra buttons to the camera view.

Simply listen for this notification which is fired several times whenever the camera is changed:

[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(cameraChanged:)
                                                 name:@"AVCaptureDeviceDidStartRunningNotification"
                                               object:nil];

Then use this method to flip the camera view:

- (void)cameraChanged:(NSNotification *)notification
{
    if(imagePicker.cameraDevice == UIImagePickerControllerCameraDeviceFront)
    {
        imagePicker.cameraViewTransform = CGAffineTransformIdentity;
        imagePicker.cameraViewTransform = CGAffineTransformScale(imagePicker.cameraViewTransform, -1,     1);
    } else {
        imagePicker.cameraViewTransform = CGAffineTransformIdentity;
    }
}

2 Comments

I couldn't find the above-mentioned hard-coded "AVCaptureDeviceDidStartRunningNotification" notification name in the AVFoundation headers (even though it works) so I'm not sure how long it will be supported for. But I did find an alternative with the AVCaptureSessionDidStartRunningNotification constant in AVFoundation/AVCaptureSession.h. I needed to run the content of cameraChanged: on the main thread with dispatch_async(dispatch_get_main_queue(),…) to get the transform to take effect.
Thanks @Darren, pretty amazing answer, works like a charm when translated to Swift (tested with iOS8.2 and an iPhone 5c).
6

I know this question is really old but it seems like this is a still a common problem. Just set a CGAffineTransform on the cameraViewTransform property on a UIImagePickerController object.

let picker = UIImagePickerController()
picker.cameraViewTransform = CGAffineTransformScale(picker.cameraViewTransform, -1, 1)

Comments

5

Updated "bandog" answer for swift 4

let picker = UIImagePickerController()
picker.cameraViewTransform = picker.cameraViewTransform.scaledBy(x: -1, y: 1)

1 Comment

Is this just flipping the view for the camera or it's actually showing the video in the same way even after uploading and playing video on other devices ?
3

It took me few hours, but I think I got there. Here is a working solution for Swift 5.2 of how to get correct image (both in ImagePicker preview and in output).


     //Registering to get notification when users takes a picture

    override func viewDidLoad() {
        NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object: nil, queue: nil) { (notification) in
            self.changePhotoOrientation()
    }

    //Changing image orientation for ImagePicker preview

    func changePhotoOrientation() {
        var subviews: [UIView] = [imagePicker.view]
        while (!subviews.isEmpty) {
            let subview = subviews.removeFirst()
            subviews += subview.subviews
            if (subview.isKind(of: UIImageView.self)) {
                subview.transform = self.imagePicker.cameraViewTransform.scaledBy(x: -1, y: 1)
            }
        }
    }

    //Changing image orientation for the output image

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            image = UIImage(cgImage: userPickedImage.cgImage!, scale: userPickedImage.scale, orientation: .leftMirrored)
        }
    }

}

Comments

1

It looks like AVCaptureDeviceDidStartRunningNotification is no longer available as a means of detecting camera device changes. Also, the cameraDevice property on UIImagePickerController doesn't work with KVO. However, it's still possible to detect camera device changes, as shown below (though long-term support for this solution isn't guaranteed as we're using KVO on a property that isn't explicitly marked as KVO-compliant).

import AVFoundation

var context = 0

override func viewDidLoad() {
    super.viewDidLoad()
    // Register for notifications
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(self, selector: #selector(handleCaptureSessionDidStartRunning(_:)), name: AVCaptureSessionDidStartRunningNotification, object: nil)
    notificationCenter.addObserver(self, selector: #selector(handleCaptureSessionDidStopRunning(_:)), name: AVCaptureSessionDidStopRunningNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func handleCaptureSessionDidStartRunning(notification: NSNotification) {
    guard let session = notification.object as? AVCaptureSession else { return }
    session.addObserver(self, forKeyPath: "inputs", options: [ .Old, .New ], context: &context)
}

func handleCaptureSessionDidStopRunning(notification: NSNotification) {
    guard let session = notification.object as? AVCaptureSession else { return }
    session.removeObserver(self, forKeyPath: "inputs")
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if context == &self.context {
        if let inputs = change?[NSKeyValueChangeNewKey] as? [AnyObject], captureDevice = (inputs.first as? AVCaptureDeviceInput)?.device {
            switch captureDevice.position {
            case .Back: print("Switched to back camera")
            case .Front: print("Switched to front camera")
            case .Unspecified: break
            }
        }
    } else {
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }
}

Swift 4+ version:

import AVFoundation

var context = 0

override func viewDidLoad() {
    super.viewDidLoad()
    // Register for notifications
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStartRunningNotification"), object: nil, queue: nil) { [weak self] notification in
        self?.handleCaptureSessionDidStartRunning(notification: notification)
    }
    notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStopRunningNotification"), object: nil, queue: nil) { [weak self] notification in
        self?.handleCaptureSessionDidStopRunning(notification: notification)
    }
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

func handleCaptureSessionDidStartRunning(notification: Notification){
    guard let session = notification.object as? AVCaptureSession else { return }
    session.addObserver(self, forKeyPath: "inputs", options: [ .old, .new ], context: &context)
}

func handleCaptureSessionDidStopRunning(notification: Notification){
    guard let session = notification.object as? AVCaptureSession else { return }
    session.removeObserver(self, forKeyPath: "inputs")
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &self.context {
        if let inputs = change?[NSKeyValueChangeKey.newKey] as? [AnyObject], let captureDevice = (inputs.first as? AVCaptureDeviceInput)?.device {
            switch captureDevice.position {
            case .back: print("Switched to back camera")
            case .front: print("Switched to front camera")
            case .unspecified: break
            }
        }
    } else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

2 Comments

Thanks, man! You saved my day! May 2020, your answer still works.
This answer does not appear to make any attempt to answer the question about the captured image being flipped.
0

Full Working Example in Swift, which answers to the initial question of this post (tested on an iPhone 5c using iOS 8.2):

        import UIKit

        class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIActionSheetDelegate {

         @IBOutlet var myUIImageView: UIImageView!

         var myUIImagePickerController: UIImagePickerController!

         override func viewDidLoad() {
             super.viewDidLoad()
         }

         override func viewWillAppear(animated: Bool) {
             println("viewWillAppear(animated: Bool) method called.")
             super.viewWillAppear(animated)
             NSNotificationCenter.defaultCenter().removeObserver(self)
         }

         override func viewWillDisappear(animated: Bool) {
             println("viewWillDisappear(animated: Bool) method called.")
             super.viewWillDisappear(animated)
             NSNotificationCenter.defaultCenter().addObserver(self, selector: "cameraChanged:", name: "AVCaptureDeviceDidStartRunningNotification", object: nil)
         }

         /* UIImagePickerControllerDelegate Section */
         func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
              if(self.myUIImagePickerController.sourceType == UIImagePickerControllerSourceType.Camera) {
                 self.myUIImageView.image = info[UIImagePickerControllerEditedImage] as? UIImage
              } else {
                 self.myUIImageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
              }
              self.dismissViewControllerAnimated(true, completion: nil)
         }

        func imagePickerControllerDidCancel(picker: UIImagePickerController) {
            self.dismissViewControllerAnimated(true, completion: nil)
        }

        /*
        You can choose to use one of the UIResponder methods:
        touchesBegan, touchesMoved, touchesEnded etc, in order to detect the touch
        on the UIImageView.
        */
        override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
            let touch: UITouch? = touches.anyObject() as? UITouch
            if (touch?.view == myUIImageView) {
                println("myUIImageView has been tapped by the user.")
                self.takingAPictureUsingTheCamera()
            }
        }

        func takingAPictureUsingTheCamera() {
            self.myUIImagePickerController = UIImagePickerController()
            self.myUIImagePickerController.delegate = self // Set the delegate
            self.myUIImagePickerController.sourceType = UIImagePickerControllerSourceType.Camera
            self.myUIImagePickerController.cameraDevice = UIImagePickerControllerCameraDevice.Front
    //        self.myUIImagePickerController.editing = true
            self.myUIImagePickerController.allowsEditing = true
            self.presentViewController(self.myUIImagePickerController, animated: true, completion: nil)
        }

        func cameraChanged(notification: NSNotification) {
             println("cameraChanged(notification: NSNotification) method called.")
             self.myUIImagePickerController.cameraViewTransform = CGAffineTransformIdentity
             if(self.myUIImagePickerController.cameraDevice == UIImagePickerControllerCameraDevice.Front){
                 self.myUIImagePickerController.cameraViewTransform = CGAffineTransformScale(self.myUIImagePickerController.cameraViewTransform, -1, 1)
             }
        }
       }// End class

Comments

0

Here are full example code of working view controller, which can make back-front photos and mirror front-photos.

import SwiftUI
import PhotosUI

import UIKit
import AVFoundation

struct ImagePicker: UIViewControllerRepresentable {

    enum PickerType {
        case image(UIImage)
    }

    private let callback: (PickerType?) -> Void

    init(callback: @escaping (PickerType?) -> Void) {
        self.callback = callback
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(controller: self)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIViewController {
        let picker = CameraCaptureViewController()
        picker.cameraDelegate = context.coordinator
        picker.sourceType = .camera
        picker.delegate = context.coordinator

        return picker
    }

    func updateUIViewController(_ uiViewController: UIViewController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {
    }

    // MARK: - Coordinator

    final class Coordinator: NSObject, UINavigationControllerDelegate {
        var controller: ImagePicker
        var lastKnownPosition = AVCaptureDevice.Position.unspecified

        init(controller: ImagePicker) {
            self.controller = controller
        }
    }

}

// MARK: - UIImagePickerControllerDelegate

extension ImagePicker.Coordinator: UIImagePickerControllerDelegate, CameraCaptureViewControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController,
                               didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        guard var userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }

        if lastKnownPosition == .front, let cgImage = userPickedImage.cgImage {
            userPickedImage = UIImage(cgImage: cgImage, scale: userPickedImage.scale, orientation: .leftMirrored)
        }

        controller.callback(.image(userPickedImage))
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        controller.callback(nil)
    }

    func cameraPositionDidChanged(newPosition: AVCaptureDevice.Position) {
        lastKnownPosition = newPosition
    }

}

protocol CameraCaptureViewControllerDelegate {
    func cameraPositionDidChanged(newPosition: AVCaptureDevice.Position)
}

class CameraCaptureViewController: UIImagePickerController {

    var lastKnownPosition = AVCaptureDevice.Position.unspecified {
        didSet {
            cameraDelegate?.cameraPositionDidChanged(newPosition: lastKnownPosition)
        }
    }

    var cameraDelegate: CameraCaptureViewControllerDelegate?
    weak private var session: AVCaptureSession?
    private var context = 0

    deinit {
        session?.removeObserver(self, forKeyPath: "inputs")
        NotificationCenter.default.removeObserver(self)
    }

    override func viewDidLoad() {
        let notificationCenter = NotificationCenter.default

        notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object: nil, queue: nil) { [weak self] (notification) in
            if self?.lastKnownPosition == .front {
                self?.changePhotoOrientation()
            }
        }

        notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStartRunningNotification"), object: nil, queue: nil) { [weak self] notification in
            self?.handleCaptureSessionDidStartRunning(notification: notification)
        }
        notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStopRunningNotification"), object: nil, queue: nil) { [weak self] notification in
            self?.handleCaptureSessionDidStopRunning(notification: notification)
        }
    }

    // Changing image orientation for ImagePicker preview
    func changePhotoOrientation() {
        var subviews: [UIView] = [view]

        while !subviews.isEmpty {
            let subview = subviews.removeFirst()
            subviews += subview.subviews
            if (subview.isKind(of: UIImageView.self)) {
                subview.transform = cameraViewTransform.scaledBy(x: -1, y: 1)
            }
        }
    }

    func handleCaptureSessionDidStartRunning(notification: Notification){
        guard let session = notification.object as? AVCaptureSession else { return }
        self.session = session
        if let input = session.inputs.first as? AVCaptureDeviceInput {
            lastKnownPosition = input.device.position
        }
        session.addObserver(self, forKeyPath: "inputs", options: [ .old, .new ], context: &context)
    }

    func handleCaptureSessionDidStopRunning(notification: Notification) {
        guard let session = notification.object as? AVCaptureSession else { return }
        session.removeObserver(self, forKeyPath: "inputs")
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let inputs = change?[NSKeyValueChangeKey.newKey] as? [AnyObject], let captureDevice = (inputs.first as? AVCaptureDeviceInput)?.device {
            lastKnownPosition = captureDevice.position
        }
    }

}

Comments

-2

Answers with a clause like

cameraViewTransform = cameraViewTransform.scaledBy ..

are completely wrong.

You simply set the cameraViewTransform to a value.

In fact, to an affine transform.

Hence,

var mirrored = false

@objc func userTappedToToggleDisplayMirroring() {
    mirrored = !mirrored
    cameraViewTransform = CGAffineTransform(scaleX: mirrored ? -1 : 1, y: 1)
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.