UIImagePickerController camera preview is portrait

2020-01-23 15:07发布

In my landscape-only iPhone application, I launch a UIImagePickerController to take a photo, but the live image displayed from the camera is in portrait orientation, with blank space around it. The image is rotated.

Once the camera button is pressed, the preview is very messy, with most of the preview off screen, and views not correctly aligned.

Apple has acknowledged that this is defect, and is working on it.

My question is, does anyone have a work-around (legal or illegal) that would allow me to get this working now. I wouldn't release to the App Store with an illegal fix, but I would have a much better app for user testing - currently the camera is pretty much unusable in landscape.

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

Edit - just to clarify, the image I get is correctly landscape. I want the camera & preview UIs to look right!


Camera

alt text

7条回答
冷血范
2楼-- · 2020-01-23 15:38

I don't want to rotate the image after capture; I want to have the preview show correctly in landscape mode. So in iOS 6, I allow portrait mode at the application level, but set the app's root view controller to be of class MyNonrotatingNavigationController, defined as follows:

@implementation MyNonrotatingNavigationController

-(NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

@end

So everything that's ever shown inside this nav controller will be in landscape orientation (you could do this with any view controller). Now, when I need to show an image picker, I replace the app window's root view controller with a generic one that supports portrait mode. To prevent the old root view controller and its views from deallocating, I maintain pointers to them until I'm ready to put them back in the app 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 a pain, but it does seem to work for both photos and videos. The image picker's controls show in portrait mode, but the rest of the app is landscape-only.

查看更多
欢心
3楼-- · 2020-01-23 15:39

You'll need to set custom view (with toolbar and title) as cameraOverlayView, also you'll need to set allowsEditing and showsCameraControls to NO as that will hide standard controls and the preview. That's what I've found as necessary in app I'm making (thought I need the picker to be in portrait, but I need it to apply some ui changes if user rotate his device to landscape).
Code may be provided if there's need for it.

P.S. There are still some bugs in my code, but I'm working on it :)

查看更多
聊天终结者
4楼-- · 2020-01-23 15:40

Let me know if this is discouraged re-posting the best answer above, I have a 2019 swift version 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
}
查看更多
冷血范
5楼-- · 2020-01-23 15:41

The answer is more ridiculous than you might think. I had the same problem and found a solution in a forum somewhere. Pass your taken image into a method like 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;
}

:(

查看更多
Luminary・发光体
6楼-- · 2020-01-23 15:46

I solved this issue by making the UIImagePickerController appear in full-screen mode, which is also what Apple recommends for iPad.

From UIImagePickerController documentation:

On iPad, if you specify a source type of UIImagePickerControllerSourceTypeCamera, you can present the image picker modally (full-screen) or by using a popover. However, Apple recommends that you present the camera interface only full-screen.

查看更多
甜甜的少女心
7楼-- · 2020-01-23 15:52

I don't think that you need the extra work to deal with imageRotation or the EXIF data at all. Image drawInRect will take care of that automatically.

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

查看更多
登录 后发表回答