38

I have a very simple application: - All orientations are permitted with only a button on a screen - The button show a UIImagePickerController (to take a photo) - Build with Xcode 5 and SDK 7

On iOS 8, the camera of the UIImagePickerController is appearing correctly whether I am in landscape or in portrait, but when I rotate the device, I got the camera view rotated by 90 degrees, here's a an example:

  1. I have my app in portrait
  2. I push the button that shows me the UIImagePickerController
  3. I am in the camera view and I go to landscape mode, here is what I get:

The view is in landscape but the camera is rotated by 90 degrees

Did someone else already got this issue?

PS: And if I take a photo (again in landscape), the photo is correctly taken and now correctly displayed :


EDIT

The bug seems to be fixed on my iPad running iOS 8.1 but nothing appears related to that bug in iOS 8.1 Release Notes: https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-8.1/

Thanks all for the proposed fixes for earlier versions of iOS 8!

Cœur
  • 37,241
  • 25
  • 195
  • 267
Kevin Hirsch
  • 956
  • 1
  • 9
  • 17
  • Hi Kevin....Are you launching your camera in landscape mode.The sort of issue which I am facing is that My iPhone application is in portrait mode, and in one view I have a camera button and I want to launch the camera in landscape mode only.I googled,but no luck.So I need some idea or some references so that i can apply that in my project.Thanks in advance:) – Nilesh Kumar Mar 05 '15 at 05:04
  • @wimcNilesh: this issue is not about launching the camera in a special orientation but about orientation change issue on camera screen. – Kevin Hirsch Mar 05 '15 at 12:02
  • ok got it.Thanks .Just 1more thing to ask "Can i launch my camera in landscape mode only in my iPhone application, where each view is in Portrait mode?" – Nilesh Kumar Mar 05 '15 at 12:12
  • @wimcNilesh: I don't know. You'll have to search for an answer or ask a question ;) – Kevin Hirsch Mar 05 '15 at 12:17
  • .....No problem...but thanks for quick response. – Nilesh Kumar Mar 05 '15 at 12:20
  • [Try this link may it will help... ](http://stackoverflow.com/questions/9324130/iphone-image-captured-from-camera-rotate-90-degree-automatically) – gireesh Chanti Jan 27 '16 at 12:53

12 Answers12

18

I believe it is an iOS 8 bug. For example if you open your contacts app and click edit/add photo/take photo, the same issue occurs on a standard iOS app! Post the issue to Apple support just as I have.

hitme
  • 194
  • 1
  • 6
16

I have found an another very good solution for this issue which i am using currently. You just need to pass the image as an arugument to this method after capturing image using UIImagePickerController. It works well for all version of iOS and also for both portrait and landscape orientations of Camera. It checks for EXIF property of image using UIImageOrientaiton and accordind to the value of orientation, it transforms & scales the image so you will get the same return image with same orientation as your camera view orientation.

Here i have kept maximum resolutions of 3000 so that the image quality doesn't get spoiled specially while you are using retina devices but you can change its resolution as per your requirement.

// Objective C code:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
{
     UIImage *imagePicked = [info valueForKey:UIImagePickerControllerOriginalImage];

     imagePicked = [self scaleAndRotateImage:imagePicked];

     [[self delegate] sendImage:imagePicked];
     [self.imagePicker dismissViewControllerAnimated:YES completion:nil];    
}

- (UIImage *) scaleAndRotateImage: (UIImage *)image
{
    int kMaxResolution = 3000; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);

    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef),      CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient)
    {
        case UIImageOrientationUp: //EXIF = 1
             transform = CGAffineTransformIdentity;
             break;

        case UIImageOrientationUpMirrored: //EXIF = 2
             transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
             transform = CGAffineTransformScale(transform, -1.0, 1.0);
             break;

        case UIImageOrientationDown: //EXIF = 3
             transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
             transform = CGAffineTransformRotate(transform, M_PI);
             break;

        case UIImageOrientationDownMirrored: //EXIF = 4
             transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
             transform = CGAffineTransformScale(transform, 1.0, -1.0);
             break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
             boundHeight = bounds.size.height;
             bounds.size.height = bounds.size.width;
             bounds.size.width = boundHeight;
             transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
             transform = CGAffineTransformScale(transform, -1.0, 1.0);
             transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
             break;

        case UIImageOrientationLeft: //EXIF = 6
             boundHeight = bounds.size.height;
             bounds.size.height = bounds.size.width;
             bounds.size.width = boundHeight;
             transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
             transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
             break;

        case UIImageOrientationRightMirrored: //EXIF = 7
             boundHeight = bounds.size.height;
             bounds.size.height = bounds.size.width;
             bounds.size.width = boundHeight;
             transform = CGAffineTransformMakeScale(-1.0, 1.0);
             transform = CGAffineTransformRotate(transform, M_PI / 2.0);
             break;

        case UIImageOrientationRight: //EXIF = 8
             boundHeight = bounds.size.height;
             bounds.size.height = bounds.size.width;
             bounds.size.width = boundHeight;
             transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
     }

     UIGraphicsBeginImageContext(bounds.size);

     CGContextRef context = UIGraphicsGetCurrentContext();

     if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft)
     {
         CGContextScaleCTM(context, -scaleRatio, scaleRatio);
         CGContextTranslateCTM(context, -height, 0);
     }
     else {
         CGContextScaleCTM(context, scaleRatio, -scaleRatio);
         CGContextTranslateCTM(context, 0, -height);
     }

     CGContextConcatCTM(context, transform);

     CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
     UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

     return imageCopy;
}

// Swift 4.0 Code:

func scaleAndRotateImage(image: UIImage, MaxResolution iIntMaxResolution: Int) -> UIImage {
        let kMaxResolution = iIntMaxResolution
        let imgRef = image.cgImage!
        let width: CGFloat = CGFloat(imgRef.width)
        let height: CGFloat = CGFloat(imgRef.height)
        var transform = CGAffineTransform.identity
        var bounds = CGRect.init(x: 0, y: 0, width: width, height: height)

        if Int(width) > kMaxResolution || Int(height) > kMaxResolution {
            let ratio: CGFloat = width / height
            if ratio > 1 {
                bounds.size.width = CGFloat(kMaxResolution)
                bounds.size.height = bounds.size.width / ratio
            }
            else {
                bounds.size.height = CGFloat(kMaxResolution)
                bounds.size.width = bounds.size.height * ratio
            }
        }
        let scaleRatio: CGFloat = bounds.size.width / width
        let imageSize = CGSize.init(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height))

        var boundHeight: CGFloat
        let orient = image.imageOrientation
        // The output below is limited by 1 KB.
        // Please Sign Up (Free!) to remove this limitation.

        switch orient {
        case .up:
            //EXIF = 1
            transform = CGAffineTransform.identity
        case .upMirrored:
            //EXIF = 2
            transform = CGAffineTransform.init(translationX: imageSize.width, y: 0.0)
            transform = transform.scaledBy(x: -1.0, y: 1.0)

        case .down:
            //EXIF = 3
            transform = CGAffineTransform.init(translationX: imageSize.width, y: imageSize.height)
            transform = transform.rotated(by: CGFloat(Double.pi / 2))

        case .downMirrored:
            //EXIF = 4
            transform = CGAffineTransform.init(translationX: 0.0, y: imageSize.height)
            transform = transform.scaledBy(x: 1.0, y: -1.0)
        case .leftMirrored:
            //EXIF = 5
            boundHeight = bounds.size.height
            bounds.size.height = bounds.size.width
            bounds.size.width = boundHeight
            transform = CGAffineTransform.init(translationX: imageSize.height, y: imageSize.width)

            transform = transform.scaledBy(x: -1.0, y: 1.0)
            transform = transform.rotated(by: CGFloat(Double.pi / 2) / 2.0)
            break

        default: print("Error in processing image")
        }

        UIGraphicsBeginImageContext(bounds.size)
        let context = UIGraphicsGetCurrentContext()
        if orient == .right || orient == .left {
            context?.scaleBy(x: -scaleRatio, y: scaleRatio)
            context?.translateBy(x: -height, y: 0)
        }
        else {
            context?.scaleBy(x: scaleRatio, y: -scaleRatio)
            context?.translateBy(x: 0, y: -height)
        }
        context?.concatenate(transform)
        context?.draw(imgRef, in: CGRect.init(x: 0, y: 0, width: width, height: height))
        let imageCopy = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return imageCopy!
    }
sajgan2015
  • 305
  • 3
  • 10
5

I found an easy and accurate patch to resolve this issue. Add following code before presenting UIImagePickerController:

if (iOS8_Device)
{
            if([[UIDevice currentDevice]orientation] == UIDeviceOrientationFaceUp)
            {
                if([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft)
                {
                    [[UIDevice currentDevice]setValue:[NSNumber numberWithInteger:UIDeviceOrientationLandscapeRight] forKey:@"orientation"];
                }
                else
                {
                    [[UIDevice currentDevice]setValue:[NSNumber numberWithInteger:UIDeviceOrientationLandscapeLeft] forKey:@"orientation"];
                }
            }
}

Also you need to subclass UIImagePickerController and override following method as below to work it better.

- (BOOL)shouldAutorotate
{
    [super shouldAutorotate];
    return NO;
}

After using above code it will work for both landscape orientation well and the orientation will not be changed to UIDeviceOrientationFaceUp mode.

sajgan2015
  • 305
  • 3
  • 10
  • But isn't the OP's question about an orientation issue that arises after a device is rotated? The UIImagePickerController is already presented when the problem occurs. – Tony Adams Mar 18 '15 at 14:50
  • 1
    shouldAutorotate => NO fixed the issue for me. – Robert Apr 08 '15 at 12:36
4

I believe that this is an iOS 8 Bug as hitme mentioned. I filed an Apple Ticket in regards to the example of the contacts app and made an open radar copy of it here http://openradar.appspot.com/18416803

Details of Bug Report
Summary: In the Contacts app if the user rotates the iPad device so its in landscape orientation and then lays the device flat on a desk or holds it level with the ground the Camera Viewfinder will be launched rotated 90 degrees with black bars on the sides. The user can then take the photo which appears correctly rotated. This is a terrible user experience and results in the user having lots of difficulty in capturing an image.

Steps to Reproduce:
1. Open Contacts App
2. Rotate iPad to Landscape Mode
3. Lay the iPad to flat on a desk
4. Add New Contact
5. Add Photo > Take Photo 6. Pick up the iPad

Expected Results:
Image Capture Viewfinder Displays in full screen oriented in Landscape mode.

Actual Results:
Image Capture Viewfinder is rotated 90 degrees and is not full screen.

Versions Affected: iOS 8.0, 8.0.2, & 8.1.

Polar Bear
  • 918
  • 2
  • 7
  • 19
3

The issue has been fixed in iOS 8.1 Tested with Contacts App. works fine.

But another issue I found ,

  1. Go to Contacts App
  2. Keep your device like ipad camera is facing the ground.
  3. Add contact -> Take Photo

Test it 2 3 times with FaceUp orientation, the camera view will start rotating strangely again as you can see in the Pictures uploaded above.

MaNn
  • 745
  • 4
  • 11
  • 22
  • Indeed. I edited my question for everyone to know. I did not manage to reproduce your issue. You keep the iPad always facing the ground in portrait mode to have this issue? – Kevin Hirsch Oct 22 '14 at 12:13
  • I was able to reproduce this problem with 8.1 - 1) Hold iPad so Contacts app is in portrait mode 2) Open the camera 3) Hold iPad flat on table 4) Pick up iPad and rotate into Landscape 5) Lay iPad back on table 6) Cancel the camera 7) Open the camera again - You have to have the iPad flat and pick it up and do some goofy things, but it does still happen – Chris Nov 06 '14 at 18:35
  • @KevinHirsch Yes,keep the iPad facing the ground. and try to rotate it in Landscape ,as Chris said ,do some goofy things. try to close and open camera multiple times.This issue will be reproduced – MaNn Nov 10 '14 at 05:33
  • @Mann I'll try! Don't forget to send a bug report ;) – Kevin Hirsch Nov 10 '14 at 10:12
1

I had the same issue and after getting back to basics and creating a new project which actually worked.. I tracked it down to;

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    // Had forgot to call the following..
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

Hope it helps.

PaulB
  • 962
  • 7
  • 15
1

As @hitme said, it is a bug in iOS 8, but you can work around the issue by setting -[UIImagePickerController cameraViewTransform] before presenting it:

CGFloat angle;
switch (interfaceOrientation)
{
    case UIInterfaceOrientationLandscapeLeft:
        angle = M_PI_2;
        break;

    case UIInterfaceOrientationLandscapeRight:
        angle = -M_PI_2;
        break;

    case UIInterfaceOrientationPortraitUpsideDown:
        angle = M_PI;
        break;

    default:
        angle = 0;
        break;
}
imagePicker.cameraViewTransform = CGAffineTransformMakeRotation([UIApplication sharedApplication].statusBarOrientation);
Austin
  • 5,625
  • 1
  • 29
  • 43
  • interesting... setting cameraViewTransform as shown in your snippet actually sort of works for me - it seems to address the Rotate Device after Presenting Camera problem. But it rotates all the views to 45° - to the right for portrait and to the left for landscape. Further, if I comment out the switch, it behaves exactly the same. Then I noticed that the CGFloat angle is not used in your snippet at all. How do we use it? thanks! – Tony Adams Mar 18 '15 at 15:26
0

I got the same issue. I tested on both iOS 7.1.2 and iOS 8. So far, it only happened on iPad device with iOS 8. So I temporarily fix it by following code:

// In my app, I subclass UIImagePickerController and code my own control buttons
// showsCameraControls = NO;
- (void)viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];
  [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
}

It is not recommended because it will mess up the presentingViewController's orientation.

Are there any replies from Apple yet?

Ray
  • 59
  • 2
  • 2
0

Cannot wait for Apple to fix the issue...

- (void)viewWillAppear:(BOOL)animated
{
  ...
  if (iPad & iOS 8)
  {
    switch (device_orientation)
    {
      case UIDeviceOrientationPortraitUpsideDown: self.cameraViewTransform = CGAffineTransformMakeRotation(DEGREES_RADIANS(180)); break;
      case UIDeviceOrientationLandscapeLeft: self.cameraViewTransform = CGAffineTransformMakeRotation(DEGREES_RADIANS(90)); break;
      case UIDeviceOrientationLandscapeRight: self.cameraViewTransform = CGAffineTransformMakeRotation(DEGREES_RADIANS(-90)); break;
      default: break;
    }
  }
}
Ray
  • 59
  • 2
  • 2
0

I know it's an iOS bug, but i've implemented a temporary fix:

on the view that presents the image picker, add this code if you don't get any orientation changes events (or use didRotateFromInterfaceOrientation otherwhise):

- (void)viewDidLoad {
   [super viewDidLoad];
   // ....

   if ([[UIDevice currentDevice].systemVersion floatValue] >=8) {
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
   }

}

now on rotation simply dismiss and represent your imagepicker:

- (void)didRotate:(NSNotification *)notification
{
    if (self.presentedViewController && self.presentedViewController==self.imagePickerController) {
        [self dismissViewControllerAnimated:NO completion:^{
            [self presentViewController:self.imagePickerController animated:NO completion:nil];
        }];
    }
}

works a bit rough, but this is the best solution i've found

Eliktz
  • 572
  • 1
  • 4
  • 27
0

Here's a fix I've found by adding this category to UIImagePickerController:

@implementation UIImagePickerController (Rotation)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [self viewWillAppear:NO];
    [self viewDidAppear:NO];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [[UIApplication sharedApplication] setStatusBarHidden:NO];
}

@end

I realise it is hacky calling UIViewController lifecycle methods directly, but this is the only solution I've found. Hopefully Apple will fix this soon!

Rowan Jones
  • 552
  • 5
  • 7
0

This is the code I used to fix my app that was rotating the openCV camera into landscape if the camera was started with the device on the desk in Face Up position. The camera needed to be forced into portrait mode for the app to function correctly.

- (BOOL)shouldAutorotate
 {
    [super shouldAutorotate];
    return NO;
 }

-(void)viewDidAppear:(BOOL)animated
{

    [super viewDidAppear: animated];

    if([[UIDevice currentDevice]orientation] == UIDeviceOrientationFaceUp)
        {
            if([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft)
            {
                [[UIDevice currentDevice]setValue:[NSNumber numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];
            }
            else
            {
                [[UIDevice currentDevice]setValue:[NSNumber numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];
            }
        }
}
Cosworth66
  • 597
  • 5
  • 14