【问题标题】:UIImagePickerController camera preview is portrait in landscape appUIImagePickerController 相机预览是横向应用程序中的纵向
【发布时间】:2010-10-06 23:40:17
【问题描述】:

在我的纯横向 iPhone 应用程序中,我启动了一个 UIImagePickerController 来拍照,但从相机显示的实时图像是纵向的,周围有空白区域。图像已旋转。

一旦按下相机按钮,预览就会非常混乱,大部分预览都在屏幕之外,并且视图没有正确对齐。

Apple 已经承认这是一个缺陷,并且正在努力解决。

我的问题是,有没有人有解决方法(合法或非法)可以让我现在开始工作。我不会通过非法修复将其发布到 App Store,但我会为用户测试提供更好的应用程序 - 目前相机在横向上几乎无法使用。

如果可以的话,我会附上一个简单的测试项目和图片。

编辑 - 澄清一下,我得到的图像是正确的风景。我希望相机和预览 UI 看起来正确!


【问题讨论】:

  • 像 NFL 裁判一样说话,“非法修复,进攻。10 码。第二次下降。”
  • 这很奇怪,因为我不知道 NFL 是什么。
  • 奇怪的是我们北美人(注意:北美不仅包括美国)假设每个人都知道 NFL 是什么! @JaneSales 它代表国家橄榄球联盟,实际上与你所知道的足球不同。
  • 我的问题正好相反。我得到横向预览,当我尝试使用图像时它是纵向的。
  • 这三个提示可能会有所帮助...stackoverflow.com/a/22282988/294884

标签: iphone cocoa-touch


【解决方案1】:

答案比你想象的更荒谬。我遇到了同样的问题,并在某处的论坛中找到了解决方案。将您拍摄的图像传递给这样的方法:

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

:(

【讨论】:

  • 非常感谢这个方法!轮换问题让我差点气死!
  • 我不明白这是如何解决问题的——视频/照片捕捉的预览仍然是横向的。
  • 工作得很好,谢谢。 4 年多了,不明白为什么苹果不解决这个问题。
  • 有趣。我得试试这个。但是,在我的应用程序中,我在测试时没有问题。它只是在应用程序进入 App Store 时出现。我不确定这怎么可能。
  • 有什么方式支持(UIImage)等功能?是否有任何特定链接指示上述代码?
【解决方案2】:

我认为您根本不需要额外的工作来处理 imageRotation 或 EXIF 数据。 Image drawInRect 会自动处理。

所以,你只需要得到这个尺寸或者图像,然后重新绘制成一个新的图像,就足够了。

【讨论】:

    【解决方案3】:

    我不想在捕获后旋转图像;我想在横向模式下正确显示预览。所以在 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;
            });
        }];
    }
    

    有点痛苦,但它似乎对照片和视频都有效。图像选择器的控件以纵向模式显示,但应用的其余部分仅以横向模式显示。

    【讨论】:

      【解决方案4】:

      我通过让UIImagePickerController 以全屏模式显示解决了这个问题,这也是 Apple 推荐的 iPad 的做法。

      来自UIImagePickerController documentation

      在 iPad 上,如果您指定 UIImagePickerControllerSourceTypeCamera 的源类型,您可以模态(全屏)或使用弹出框呈现图像选择器。但是,Apple 建议您仅全屏显示相机界面。

      【讨论】:

        【解决方案5】:

        如果不鼓励重新发布上述最佳答案,请告诉我,我为 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
        }
        

        【讨论】:

        • 你帮了我。谢谢
        【解决方案6】:

        您需要将自定义视图(带有工具栏和标题)设置为cameraOverlayView,还需要将allowsEditingshowsCameraControls 设置为NO,因为这将隐藏标准控件和预览。 这就是我在我正在制作的应用程序中发现的必要内容(我认为我需要选择器是纵向的,但如果用户将他的设备旋转到横向,我需要它来应用一些 UI 更改)。
        如果需要,可以提供代码。

        附:我的代码中仍有一些错误,但我正在努力解决它:)

        【讨论】:

          【解决方案7】:

          可以通过将modalPresentationStyle 设置为overCurrentContext 来解决此问题,如下所示,

           picker.modalPresentationStyle = .overCurrentContext
          

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

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

          为什么会这样?

          因为ImagePickerController 仅适用于纵向模式,当您尝试从横向视图控制器中显示选择器时,它会尝试将您的状态栏设置为纵向模式。因此,如果您将modalPresentationStyle 设置为overCurrentContext,则它不会尝试设置方向。它将考虑当前方向。

          请参阅 Apple guide line 以获取 ImagePickerController。它指出,

          UIImagePickerController 类仅支持纵向模式。此类旨在按原样使用,不支持子类化。此类的视图层次结构是私有的,不得修改,但有一个例外。您可以将自定义视图分配给 cameraOverlayView 属性,并使用该视图来显示附加信息或管理相机界面和代码之间的交互。

          【讨论】:

            猜你喜欢
            • 2018-06-17
            • 2013-05-22
            • 1970-01-01
            • 1970-01-01
            • 2011-04-19
            • 2014-09-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多