web-dev-qa-db-ja.com

iOS 8でUIImagePickerControllerカメラビューが奇妙に回転する(写真)

非常にシンプルなアプリケーションがあります:-画面上のボタンのみですべての方向が許可されています-ボタンはUIImagePickerController(写真を撮る)を表示します-Xcode 5およびSDK 7でビルドします

iOS 8では、UIImagePickerControllerのカメラが横向きでも縦向きでも正しく表示されますが、デバイスを回転させると、カメラビューを90度回転させました。以下に例を示します。

  1. アプリを縦向きにしています
  2. UIImagePickerControllerを表示するボタンを押します
  3. 私はカメラビューで、ランドスケープモードに行きます、ここに私が得るものがあります:

 ビューは横向きですが、カメラは90度回転しています

他の誰かがすでにこの問題を抱えていましたか?

PS:そして、私が写真を撮った場合(再び横向き)、写真は正しく撮られ、正しく表示されるようになりました:


[〜#〜] edit [〜#〜]

バグはiOS 8.1を実行しているiPadで修正されているようですが、iOS 8.1リリースノートではそのバグに関連するものは何も表示されません: https://developer.Apple.com/library/content/releasenotes/General/RN-iOSSDK -8.1 /

以前のバージョンのiOS 8に対して提案された修正をありがとう

38
Kevin Hirsch

これはiOS 8のバグだと思います。たとえば、連絡先アプリを開いて[写真の編集/追加/写真の撮影]をクリックすると、標準のiOSアプリでも同じ問題が発生します。問題をAppleサポートに投稿してください。

18
hitme

私が現在使用しているこの問題の別の非常に良い解決策を見つけました。 UIImagePickerControllerを使用して画像をキャプチャした後、このメソッドの引数として画像を渡すだけです。 iOSのすべてのバージョンと、カメラの縦向きと横向きの両方でうまく機能します。 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 4.0 Code:

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

この問題を解決するための簡単で正確なパッチを見つけました。 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モードに変更されません。

5
sajgan2015

Hitmeが述べたように、これはiOS 8のバグだと思います。連絡先アプリの例に関してAppleチケットを提出し、ここで公開レーダーコピーを作成しました http://openradar.appspot.com/184168

バグレポートの詳細
概要:連絡先アプリで、ユーザーがiPadデバイスを回転させて横向きにし、デバイスを机の上に平らに置くか、地面と水平に保つと、カメラビューファインダーが90度回転して黒で起動します両側のバー。ユーザーは、正しく回転して表示される写真を撮ることができます。これはひどいユーザーエクスペリエンスであり、ユーザーは画像をキャプチャするのが非常に困難になります。

再現する手順:
1。連絡先アプリを開く
2。 iPadを横向きモードに回転させる
3。 iPadを机の上に平らに置きます
4。新しい連絡先を追加
5。写真の追加>写真の撮影6. iPadを手に取ります

予想された結果:
画像キャプチャビューファインダーは、横画面モードで全画面表示されます。

実績:
イメージキャプチャビューファインダーは90度回転し、フルスクリーンではありません。

影響を受けるバージョン:iOS 8.0、8.0.2、および8.1。

4
Polar Bear

この問題は、連絡先アプリでテスト済みのiOS 8.1で修正されています。正常に動作します。

しかし、私が見つけた別の問題、

  1. 連絡先アプリに移動
  2. IPadのカメラが地面を向いているようにデバイスを保管してください。
  3. 連絡先を追加->写真を撮る

FaceUpの向きで2回3回テストすると、上記でアップロードした写真に見られるように、カメラビューが再び奇妙に回転し始めます。

3
MaNn

私は同じ問題を抱えていて、基本に戻って実際に機能する新しいプロジェクトを作成した後、追跡しました。

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

それが役に立てば幸い。

1
PaulB

@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);
1
Austin

このカテゴリを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ライフサイクルメソッドを直接呼び出すのはハッキーですが、これが唯一の解決策です。うまくいけばAppleはすぐにこれを修正します!

0
Rowan Jones

同じ問題が発生しました。 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"];
}

PresentingViewControllerの向きを台無しにするのでお勧めしません。

Appleからの返信はまだありますか?

0
Ray

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;
    }
  }
}
0
Ray

これは、カメラを机の上にあるデバイスを上向きにして起動した場合に、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"];
            }
        }
}
0
Cosworth66

私はそれがiOSのバグであることを知っていますが、一時的な修正を実装しました:

画像ピッカーを表示するビューで、方向の変更イベントを取得しない場合(または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
Eliktz