UIImagePickerController相机预览在横屏应用中为竖屏

46
在我的仅支持横屏的iPhone应用程序中,我启动了UIImagePickerController来拍照,但是从相机显示的实时图像处于纵向方向,周围有空白区域。图片被旋转了。
一旦按下相机按钮,预览就会变得非常混乱,大部分预览都超出了屏幕范围,并且视图没有正确对齐。
苹果已经承认这是一个缺陷,并正在解决它。
我的问题是,是否有人有一个(合法或非法的)解决方法,可以让我现在就让它工作?我不会使用非法修复方法发布到App Store,但是我将拥有一个更好的应用程序供用户测试 - 目前相机在横屏模式下几乎无法使用。
如果可以的话,我会附上一个简单的测试项目和图片。
编辑 - 仅为澄清,我得到的图像是正确的横向。我希望相机和预览UI看起来正确!

相机

alt text


7
听起来像是一名美式足球裁判的发言,意为“犯规行为,进攻方受罚,退十码,第二次进攻机会。” - gonzobrains
5
很奇怪,因为我不知道NFL是什么。 - Jane Sales
1
有趣的是,我们北美人(注意:北美不仅包括美国)认为每个人都知道NFL是什么!@JaneSales它代表全国橄榄球联盟,这实际上与您所知道的足球不同。 - Kevin Zych
我的问题恰恰相反。我得到了横向预览,但当我尝试使用图像时,它是纵向的。 - Aaron Bratcher
也许这三个提示可以帮助你... https://dev59.com/6mgv5IYBdhLWcg3wF89P#22282988 - Fattie
7个回答

88
答案比您想象的更荒谬。我曾遇到同样的问题,在某个论坛上找到了解决方法。将拍摄的图像传递给类似于以下方法的方法中:

答案比你想象的更荒唐。我也遇到了同样的问题,在某个论坛上找到了解决方案。将拍摄的图像传递给以下方法:

// 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;
}

:(


非常感谢这个方法!旋转问题让我快要疯了! - Paresh Masani
2
我不明白这如何解决问题 - 视频/照片捕捉的预览仍然是横向的。 - jab
工作得很好,谢谢。已经过去4年了,不明白为什么苹果不修复这个问题。 - Ser Pounce
有趣。我得试试这个。然而,在我的应用程序中,当我在测试时没有问题。只有当应用程序进入App Store时才会出现问题。我不确定这是如何可能的。 - Victor Engel
如何支持(UIImage)和其他函数?是否有特定的链接指示上述代码? - Maulik

1
我通过让UIImagePickerController以全屏模式显示来解决了这个问题,这也是苹果公司推荐的iPad上的做法。
UIImagePickerController文档中得知:

在iPad上,如果指定了UIImagePickerControllerSourceTypeCamera源类型,则可以使用全屏模态方式或使用弹出窗口呈现图像选择器。但是,苹果公司建议仅以全屏方式呈现相机界面。


1

我认为你根本不需要额外的工作来处理图像旋转或EXIF数据。Image drawInRect会自动处理这些问题。

因此,你只需要获取图像的大小并将其重新绘制到新图像中即可。


1
我不想在拍摄后旋转图像;我希望预览在横屏模式下正确显示。因此,在iOS 6中,我允许应用程序级别的纵向模式,但将应用程序的根视图控制器设置为MyNonrotatingNavigationController类,定义如下:
@implementation MyNonrotatingNavigationController

-(NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

@end

所以,所有在此导航控制器中显示的内容都将是横向方向(您可以对任何视图控制器执行此操作)。现在,当我需要显示图像选择器时,我会用支持纵向模式的通用视图控制器替换应用程序窗口的根视图控制器。为了防止旧的根视图控制器及其视图被释放,我将它们的指针保留到我准备好将它们放回应用程序窗口为止。
#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;
        });
    }];
}

有点麻烦,但似乎适用于照片和视频。图像选择器的控件在纵向模式下显示,但应用程序的其余部分仅支持横向模式。

1

如果重新发布上面的最佳答案是不被鼓励的,请让我知道,我有一个针对Alex Wayne答案的2019年swift版本。

/**
 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
}

1
你帮了我一把,谢谢。 - user139816

0

你需要设置自定义视图(包括工具栏和标题)作为cameraOverlayView,同时你需要将allowsEditingshowsCameraControls设置为NO,这样会隐藏标准控件和预览。

这是我在制作应用程序时发现的必要条件(尽管我需要选择器处于纵向状态,但如果用户将设备旋转到横向状态,我需要应用一些UI更改)。

如果需要,可以提供代码。

P.S. 我的代码仍然存在一些错误,但我正在努力解决 :)


0
这个问题可以通过将modalPresentationStyle设置为overCurrentContext来解决,如下所示:
 picker.modalPresentationStyle = .overCurrentContext

并在Main Thread上展示您的图像选择器,例如:

DispatchQueue.main.async {

   self.present(picker, animated: true) {

   }
}

为什么会发生这种情况?

因为 ImagePickerController 只在竖屏模式下工作,当您尝试从横屏 viewController 中呈现 picker 时,它会尝试将您的状态栏设置为竖屏模式。因此,如果您将 modalPresentationStyle 设置为 overCurrentContext,则它将不会尝试设置方向。它将考虑当前方向。

请参阅 Apple 指南 获取有关 ImagePickerController 的更多信息。它说明:

UIImagePickerController 类仅支持纵向模式。该类旨在按原样使用,并且不支持子类化。此类的视图层次结构是私有的,不能进行修改,只有一种例外情况。您可以将自定义视图分配给 cameraOverlayView 属性,并使用该视图呈现其他信息或管理相机界面与您的代码之间的交互。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接