UIImagePickerController camera preview - portrait in landscape application

In my landscape-only iPhone application, I run UIImagePickerController to take a picture, but the image displayed from the camera in real time is in portrait orientation with empty space around it. The image is rotated.

Once the camera button is pressed, the preview becomes very dirty, with most of the preview being off-screen and the views are not aligned correctly.

Apple admitted that this is a defect, and is working on it.

My question is: does anyone have a workaround (legal or illegal) that would allow me to get this job now. I would not have released an illegal fix in the App Store, but I would have a much better application for user testing - at present the camera is practically unsuitable for use in the landscape.

I will attach a simple test project and images if I can.

Edit - just to clarify, the image I get is correct in landscape orientation. I want the camera and preview to look right!




Camera

alt text

+45
iphone cocoa-touch
Feb 11 '09 at 18:08
source share
7 answers

The answer is more ridiculous than you think. I had the same problem and found a solution on some forum. Pass the resulting image into a method similar to this:

// Code from: http://discussions.apple.com/thread.jspa?messageID=7949889 - (UIImage *)scaleAndRotateImage:(UIImage *)image { int kMaxResolution = 640; // 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 = roundf(bounds.size.width / ratio); } else { bounds.size.height = kMaxResolution; bounds.size.width = roundf(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; } 

: (

+88
Feb 11 '09 at 18:14
source share

I don’t think you need extra work to work with imageRotation or EXIF ​​data. Image drawInRect will automatically take care of this.

So, you only need to get this size or image and redraw it to a new image, that would be enough.

+1
May 23 '12 at 6:13
source share

I do not want to rotate the image after capture; I want the preview to show correctly in landscape mode. So, in iOS 6, I enable portrait mode at the application level, but install the root application view controller in the MyNonrotatingNavigationController class, defined as follows:

 @implementation MyNonrotatingNavigationController -(NSUInteger) supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; } @end 

So, everything that is ever displayed inside this controller will be in landscape orientation (you can do this using any kind of controller). Now that I need to display the image picker, I am replacing the root window controller of the application window with a general one that supports portrait mode. To prevent the removal of the old root view controller and its views, I maintain pointers to them until I am ready to return them to the application window.

 #define APP_DELEGATE ((MyAppDelegate*)[[UIApplication sharedApplication] delegate]) static UIViewController *pickerContainer = nil; static UIViewController *oldRoot = nil; static UIView *holdView = nil; -(void) showPicker { ...create UIImagePickerController... oldRoot = APP_DELEGATE.window.rootViewController; holdView = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds]; [holdView addSubview:oldRoot.view]; pickerContainer = [UIViewController new]; pickerContainer.view = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds]; APP_DELEGATE.window.rootViewController = pickerContainer; [pickerContainer presentViewController:picker animated:YES completion:NULL]; } -(void) imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info { [pickerContainer dismissViewControllerAnimated:YES completion:^{ dispatch_async( dispatch_get_main_queue(), ^{ APP_DELEGATE.window.rootViewController = oldRoot; [APP_DELEGATE.window addSubview:oldRoot.view]; pickerContainer = nil; oldRoot = nil; holdView = nil; }); }]; } 

Kind of pain, but it seems to work for both photos and video. Image selection controls are displayed in portrait mode, but the rest of the application has only landscape orientation.

+1
Jan 30 '13 at 23:09
source share

I solved this problem by creating a UIImagePickerController in full screen, which Apple also recommends for the iPad.

From the UIImagePickerController documentation :

On the iPad, if you specify the source type of UIImagePickerControllerSourceTypeCamera, you can present the default image picker (full screen) or using popover. However, Apple recommends that you display the camera interface only in full screen mode.

+1
Mar 12 '15 at 7:50
source share

You will need to configure the custom view (with the toolbar and name) as cameraOverlayView , you will also need to set allowsEditing and showsCameraControls to NO as this will hide the standard controls and the preview. This is what I found, as necessary, in the application that I am doing (I thought that I need the collector to be in the portrait, but I need it to apply some ui changes if the user rotates his device to the landscape).
If necessary, a code can be provided.

PS There are still errors in my code, but I'm working on it :)

0
Jul 15 '11 at 18:38
source share

This problem can be solved by setting modalPresentationStyle to overCurrentContext as overCurrentContext below:

  picker.modalPresentationStyle = .overCurrentContext 

and present your image picker in Main Thread as,

 DispatchQueue.main.async { self.present(picker, animated: true) { } } 

Why is this happening?

Since ImagePickerController only works in portrait mode, and when you try to present a picker from the landscape view controller, it tries to set your status bar in portrait mode. So, if you set modalPresentationStyle to overCurrentContext it will not try to set the orientation. This will take into account the current orientation.

see the Apple manual for ImagePickerController . It is said

The UIImagePickerController class only supports portrait mode. This class is intended to be used as is and does not support subclassing. The presentation hierarchy for this class is private and should not be changed, with one exception. You can assign your own view to the cameraOverlayView property and use this view to present additional information or control the interactions between the camera interface and your code.

0
Feb 19 '19 at 9:37
source share

Let me know if it is not recommended to republish the best answer above, I have a quick version 2019 for Alex Wayne's answer.

 /** Scale and rotate a UIImage so that it is correctly oriented :param: image: The image to be rotated :returns: UIImage */ func scaleAndRotateImage(_ image: UIImage) -> UIImage { let kMaxResolution: CGFloat = 640 let imgRef: CGImage = image.cgImage! let width: CGFloat = CGFloat(imgRef.width) let height: CGFloat = CGFloat(imgRef.height) var transform: CGAffineTransform = CGAffineTransform.identity var bounds: CGRect = CGRect(x: 0, y: 0, width: width, height: height) if width > kMaxResolution || height > kMaxResolution { let ratio: CGFloat = 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) } } let scaleRatio: CGFloat = bounds.size.width / width let imageSize: CGSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height)) var boundHeight: CGFloat let orient: UIImage.Orientation = image.imageOrientation switch orient { case UIImageOrientation.up: transform = CGAffineTransform.identity case UIImageOrientation.upMirrored: transform = CGAffineTransform(translationX: imageSize.width, y: 0.0) transform = transform.scaledBy(x: -1.0, y: 1.0) case UIImageOrientation.down: transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height) transform = transform.rotated(by: CGFloat(Double.pi)) case UIImageOrientation.downMirrored: transform = CGAffineTransform(translationX: 0.0, y: imageSize.height) transform = transform.scaledBy(x: 1.0, y: -1.0) case UIImageOrientation.leftMirrored: boundHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = boundHeight transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width) transform = transform.scaledBy(x: -1.0, y: 1.0) transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0)) case UIImageOrientation.left: boundHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = boundHeight transform = CGAffineTransform(translationX: 0.0, y: imageSize.width) transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0)) case UIImageOrientation.rightMirrored: boundHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = boundHeight transform = CGAffineTransform(scaleX: -1.0, y: 1.0) transform = transform.rotated(by: CGFloat(.pi / 2.0)) case UIImageOrientation.right: boundHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = boundHeight transform = CGAffineTransform(translationX: imageSize.height, y: 0.0) transform = transform.rotated(by: CGFloat(.pi / 2.0)) } UIGraphicsBeginImageContext(bounds.size) let context: CGContext = UIGraphicsGetCurrentContext()! if orient == UIImage.Orientation.right || orient == UIImage.Orientation.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) UIGraphicsGetCurrentContext()?.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height)) let imageCopy: UIImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return imageCopy } 
0
Jul 20 '19 at 23:10
source share



All Articles