UIImagePickerController在iOS 8上拍摄视图旋转异常(图片)

38

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

iOS 8 上,UIImagePickerController 的相机无论我处于横向模式还是纵向模式下都正常显示,但当我旋转设备时,相机视图会旋转 90 度,以下是示例:

  1. 我的应用程序处于竖屏模式
  2. 我按下按钮,显示了 UIImagePickerController
  3. 我在相机视图中并进入横屏模式,这就是我得到的:

视图处于横屏模式,但相机旋转了 90 度

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

PS:如果我再次拍摄照片(同样在横屏模式下),则照片会被正确拍摄,并且现在会正确显示:


编辑

似乎我的 iPad 运行的 iOS 8.1 上修复了此错误,但与该错误相关的内容在 iOS 8.1 发布说明中并未出现:https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-8.1/

感谢所有人 提供了针对早期 iOS 8 版本的解决方法!


嗨,凯文......你是否在横向模式下启动相机。我所面临的问题是,我的iPhone应用程序处于纵向模式,在一个视图中我有一个相机按钮,我只想以横向模式启动相机。我尝试了谷歌搜索,但没有取得成功。因此,我需要一些想法或一些参考资料,以便我可以将其应用到我的项目中。提前致谢:) - Nilesh Kumar
@wimcNilesh:这个问题不是关于在特定方向启动相机,而是关于相机屏幕方向变化的问题。 - Kevin Hirsch
好的,明白了。谢谢。我还有一个问题要问:“我能在我的 iPhone 应用程序中仅以横向模式启动摄像头吗?其中每个视图都是纵向模式?” - Nilesh Kumar
@wimcNilesh:我不知道。你需要自己去查找答案或者提出问题;) - Kevin Hirsch
没问题,但感谢您的快速回复。 - Nilesh Kumar
请尝试使用此链接,也许能够帮到您:https://dev59.com/V2ox5IYBdhLWcg3wQSIO - gireesh Chanti
12个回答

18

我认为这是 iOS 8 的一个bug。例如,如果您打开通讯录应用程序并单击编辑/添加照片/拍摄照片,标准 iOS 应用程序上会出现同样的问题!像我一样将问题发布到 Apple 支持上。


谢谢您的帮助!我会立即将这个问题提交给苹果。 - Kevin Hirsch
4
仅影响我的iPad mini,iPhone 5s没有受到影响。我相信这是iPad的一个漏洞。 - hitme
嘿...我在iOS8上的iPad应用程序上也遇到了类似的问题。有人向苹果报告了这个错误吗?如果是的话,我可以得到一个链接吗? - anshul
1
我也遇到了iPad应用程序的同样问题...你使用的是哪一代iPad发现这个错误?它只出现在某些型号上吗? - chritaso
4
在联系人应用程序中似乎已经解决了这个问题。但是我的应用程序仍然存在这个问题,即使在8.1版本中也是如此。 - Jean Paul
显示剩余8条评论

16
我找到了另一个非常好的解决方案,可以解决这个问题。在使用UIImagePickerController捕获图像后,您只需要将图像作为参数传递给此方法即可。它适用于所有版本的iOS以及相机的横向和纵向方向。 使用UIImageOrientaiton检查图像的EXIF属性,并根据方向的值转换和缩放图像,因此您将获得与相机视图方向相同的具有相同方向的返回图像。
在此我保持最大分辨率为3000,以便在使用视网膜设备时不会破坏图像质量,但您可以根据需要更改其分辨率。
//Objective 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!
    }

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模式。

但是,OP的问题不是关于设备旋转后出现的方向问题吗?UIImagePickerController在问题发生时已经被呈现。 - Tony Adams
1
shouldAutorotate => NO 对我的问题进行了修复。 - Robert

4
我认为这是iOS 8的一个Bug,就像hitme所提到的。我已经提交了苹果工单,针对联系人应用程序的示例并在此处制作了一个开放性雷达副本http://openradar.appspot.com/18416803 Bug报告细节: 摘要: 在联系人应用程序中,如果用户将iPad设备旋转到横向方向,然后将设备平放在桌面上或与地面持平,相机取景器将以黑色条纹的90度旋转方式启动。然后,用户可以拍照,照片会正确地旋转显示。这是一个非常糟糕的用户体验,导致用户在捕捉图像时遇到很多困难。
复现步骤: 1. 打开联系人应用程序 2. 将iPad旋转到横向模式 3. 将iPad平放在桌面上 4. 添加新联系人 5. 添加照片>拍照 6. 拿起iPad
期望结果: 图像捕获取景器以全屏幕方式横向显示。
实际结果: 图像捕获取景器旋转了90度,并且不是全屏幕。
受影响的版本: iOS 8.0、8.0.2和8.1。

苹果将我报告的问题标记为“17075455(开放)的重复项”。 - Polar Bear
此问题已在iOS 9 Beta 1中得到解决。在联系人应用程序中,这不再是一个问题。 - Polar Bear

3
问题已在iOS 8.1中解决。使用通讯录应用测试,正常工作。
但我发现另一个问题,
1.打开通讯录应用 2.将设备保持像ipad相机面朝地面。 3.添加联系人->拍照
使用FaceUp方向测试2-3次,相机视图会再次开始奇怪地旋转,如上载的图片所示。

确实。我编辑了我的问题让大家知道。我没有成功复制你的问题。你是一直让iPad保持竖屏向下才会出现这个问题吗? - Kevin Hirsch
我能够在8.1上重现这个问题 - 1)拿起iPad,使联系人应用程序处于纵向模式2)打开相机3)将iPad平放在桌子上4)拿起iPad并旋转到横向5)将iPad放回桌子上6)取消相机7)再次打开相机 - 您必须让iPad平放并拿起它并做一些傻瓜般的事情,但它仍然会发生。 - Chris
@KevinHirsch 是的,请将iPad保持面朝地面。像Chris说的那样,尝试在横向模式下旋转它,做一些滑稽的事情。尝试多次打开和关闭相机。这个问题将会被复制。 - MaNn
@Mann 我会尝试的!别忘了发送错误报告 ;) - Kevin Hirsch

1

我曾经遇到过同样的问题,后来回归基础,创建了一个新项目并使其正常运行。我发现问题出在:

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

希望它有所帮助。

1

正如 @hitme 所说,这是 iOS 8 中的一个 bug,但你可以通过在展示之前设置 -[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);

有趣...在你的代码片段中设置cameraViewTransform似乎对我有点用 - 它似乎解决了Presenting Camera后旋转设备的问题。但它会将所有视图旋转45度 - 竖屏向右,横屏向左。此外,如果我注释掉switch,它的行为完全相同。然后我注意到CGFloat角度在你的代码片段中根本没有使用。我们该如何使用它?谢谢! - Tony Adams

0

迫不及待地等待苹果修复这个问题...

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

0

我知道这是iOS的一个bug,但我已经实现了一个临时解决方案:

在呈现图像选择器的视图上,如果您没有收到任何方向更改事件(或者使用didRotateFromInterfaceOrientation方法),请添加以下代码:

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

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

}

现在轮换简单地关闭并重新表示您的图像选择器:

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

工作有些粗糙,但这是我找到的最佳解决方案。


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生命周期方法并不是一个好的解决方案,但这是我找到的唯一解决方案。希望苹果能尽快修复这个问题!


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