UIImagePickerController相机视图在iOS 8上奇怪地旋转(图片)

时间:2014-09-19 11:14:07

标签: ios objective-c uiimagepickercontroller ios8 screen-rotation

我有一个非常简单的应用程序: - 只需一个屏幕上的按钮即可允许所有方向 - 按钮显示UIImagePickerController(拍照) - 使用Xcode 5和SDK 7构建

iOS 8 上,无论我是横向还是纵向,UIImagePickerController的相机都会正确显示,但当我旋转设备时,我将相机视图旋转了90度,这是一个例子:

  1. 我的应用程序是纵向的
  2. 我按下显示UIImagePickerController
  3. 的按钮
  4. 我在相机视图中,我进入横向模式,这是我得到的:
  5. 视图处于横向状态,但相机旋转了90度

    其他人是否已经遇到过此问题?

    PS:如果我拍照(再次在风景中),照片会被正确拍摄并且现在正确显示:


    修改

    这个错误似乎在运行iOS 8.1的iPad上得到修复,但似乎没有任何内容与iOS 8.1发行说明中的​​错误有关:https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-8.1/

    感谢所有提供针对早期版本的iOS 8的建议修补程序!

12 个答案:

答案 0 :(得分:18)

我认为这是iOS 8的错误。例如,如果您打开联系人应用程序并单击编辑/添加照片/拍照,则在标准iOS应用程序上会出现同样的问题!像我一样将问题发布到Apple支持。

答案 1 :(得分:16)

我找到了另一个非常好的解决方案,我目前正在使用这个问题。在使用UIImagePickerController捕获图像后,您只需将图像作为arugument传递给此方法。它适用于所有iOS版本,也适用于Camera的纵向和横向。它使用UIImageOrientaiton检查图像的EXIF属性,并且取决于方向的值,它转换为&缩放图像,以便获得与相机视图方向相同方向的相同返回图像。

在这里,我保持最高分辨率为3000,以便在使用视网膜设备时不会特别破坏图像质量,但您可以根据需要更改其分辨率。

//目标C代码:

- (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代码:

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!
    }

答案 2 :(得分:5)

我找到了一个简单而准确的补丁来解决此问题。在呈现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"];
                }
            }
}

此外,你需要子类化UIImagePickerController并覆盖以下方法,以便更好地工作。

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

使用上面的代码后,它既适用于横向,也不会更改为UIDeviceOrientationFaceUp模式。

答案 3 :(得分:4)

我认为这是一个iOS 8 Bug,如上所述。我提交了一份关于联系人应用程序示例的Apple Ticket,并在此处制作了一份开放的雷达副本http://openradar.appspot.com/18416803

错误报告详情
摘要: 在“联系人”应用程序中,如果用户将iPad设备旋转至横向,然后将设备平放在桌面上或将其与地面保持水平,则相机取景器将以90度旋转,侧面带有黑条。然后,用户可以拍摄出正确旋转的照片。这是一种糟糕的用户体验,导致用户在捕获图像时遇到很多困难。

重现步骤:
1.打开联系人App
2.将iPad旋转到横向模式
3.将iPad平放在桌子上 4.添加新联系人
5.添加照片>拍照 6.拿起iPad

预期结果:
图像捕获取景器以横向模式显示全屏。

实际结果:
图像捕捉取景器旋转90度,不是全屏。

受影响的版本: iOS 8.0,8.0.2和& 8.1。

答案 4 :(得分:3)

此问题已在iOS 8.1中修复 使用Contacts App进行测试。工作正常。

但是我发现了另一个问题,

  1. 转到“通讯录应用”
  2. 让您的设备像ipad相机一样面向地面。
  3. 添加联系人 - >拍照
  4. 使用FaceUp方向测试2次3次,相机视图将再次开始奇怪地旋转,如上图所示的图片所示。

答案 5 :(得分:1)

我遇到了同样的问题,在回到基础并创建一个实际工作的新项目之后......我追踪到了它;

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

希望它有所帮助。

答案 6 :(得分:1)

正如@hitme所说,这是iOS 8中的一个错误,但您可以在呈现之前设置-[UIImagePickerController cameraViewTransform]来解决此问题:

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);

答案 7 :(得分:0)

我遇到了同样的问题。我在iOS 7.1.2和iOS 8上都进行了测试。到目前为止,它只发生在iOS 8的iPad设备上。所以我通过以下代码暂时修复它:

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

不建议这样做,因为它会破坏presentViewController的方向。

Apple有没有回复?

答案 8 :(得分:0)

不能等待Apple解决问题......

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

答案 9 :(得分:0)

我知道这是一个iOS错误,但我已经实施了一个临时修复:

在显示图像选择器的视图上,如果您没有获得任何方向更改事件(或使用didRotateFromInterfaceOrientation otherwhise),请添加此代码:

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

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

}

现在轮换,只需解雇并代表你的imagepicker:

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

工作有点粗糙,但这是我发现的最佳解决方案

答案 10 :(得分:0)

通过将此类别添加到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

我意识到直接调用UIViewController生命周期方法是hacky,但这是我发现的唯一解决方案。希望Apple能尽快解决这个问题!

答案 11 :(得分:0)

这是我用来修复我的应用程序的代码,如果在面朝上位置桌面上的设备启动了相机,则将openCV摄像机旋转到横向。需要将相机强制进入纵向模式才能使应用程序正常运行。

- (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"];
            }
        }
}