web-dev-qa-db-fra.com

Vue de la caméra UIImagePickerController tournant étrangement sur iOS 8 (images)

J'ai une application très simple: - Toutes les orientations sont autorisées avec seulement un bouton sur un écran - Le bouton affiche un UIImagePickerController (pour prendre une photo) - Construire avec Xcode 5 et SDK 7

Sur iOS 8 , la caméra du UIImagePickerController apparaît correctement que je sois en paysage ou en portrait, mais lorsque je fais pivoter l'appareil , J'ai fait pivoter la vue de la caméra de 90 degrés, voici un exemple:

  1. J'ai mon application en mode portrait
  2. I Appuyez sur le bouton qui me montre le UIImagePickerController
  3. Je suis en vue caméra et je passe en mode paysage, voici ce que j'obtiens:

  La vue est en paysage mais la caméra pivote de 90 degrés

Quelqu'un d'autre a-t-il déjà eu ce problème?

PS: Et si je prends une photo (encore en paysage), la photo est correctement prise et maintenant correctement affichée:


[~ # ~] modifier [~ # ~]

Le bogue semble être corrigé sur mon iPad exécutant iOS 8.1, mais rien ne semble lié à ce bogue dans les notes de version iOS 8.1: https://developer.Apple.com/library/content/releasenotes/General/RN-iOSSDK -8,1 /

Merci à tous pour les correctifs proposés pour les versions antérieures d'iOS 8!

38
Kevin Hirsch

Je pense que c'est un bug iOS 8. Par exemple, si vous ouvrez votre application de contacts et cliquez sur éditer/ajouter une photo/prendre une photo, le même problème se produit sur une application iOS standard! Publiez le problème sur Apple support comme moi.

18
hitme

J'ai trouvé une autre très bonne solution pour ce problème que j'utilise actuellement. Vous avez juste besoin de passer l'image comme argument à cette méthode après avoir capturé l'image à l'aide de UIImagePickerController. Il fonctionne bien pour toutes les versions d'iOS et également pour les orientations portrait et paysage de l'appareil photo. Il vérifie la propriété EXIF ​​de l'image en utilisant UIImageOrientaiton et selon la valeur de l'orientation, il transforme et met à l'échelle l'image afin que vous obteniez la même image de retour avec la même orientation que l'orientation de la vue de votre caméra.

Ici, j'ai gardé des résolutions maximales de 3000 afin que la qualité d'image ne soit pas gâchée spécialement lorsque vous utilisez des appareils de rétine, mais vous pouvez changer sa résolution selon vos besoins.

// Code objectif 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 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

J'ai trouvé un correctif facile et précis pour résoudre ce problème. Ajoutez le code suivant avant de présenter 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"];
                }
            }
}

Vous devez également sous-classer UIImagePickerController et remplacer la méthode suivante comme ci-dessous pour mieux fonctionner.

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

Après avoir utilisé le code ci-dessus, cela fonctionnera bien pour l'orientation paysage et l'orientation ne sera pas modifiée en mode UIDeviceOrientationFaceUp.

5
sajgan2015

Je crois que c'est un bug iOS 8 comme hitme l'a mentionné. J'ai déposé un Apple ticket en ce qui concerne l'exemple de l'application de contacts et en ai fait une copie radar ouverte ici http://openradar.appspot.com/184168

Détails du rapport de bogue
Résumé: Dans l'application Contacts, si l'utilisateur fait pivoter l'appareil iPad de manière à ce qu'il soit en orientation paysage, puis pose l'appareil à plat sur un bureau ou le maintient au niveau du sol, le viseur de l'appareil photo sera lancé pivoté de 90 degrés avec du noir barres sur les côtés. L'utilisateur peut alors prendre la photo qui apparaît correctement pivotée. Il s'agit d'une expérience utilisateur terrible et les utilisateurs ont beaucoup de difficulté à capturer une image.

Étapes à reproduire:
1. Ouvrir l'application Contacts
2. Faire pivoter l'iPad en mode paysage
3. Posez l'iPad à plat sur un bureau
4. Ajouter un nouveau contact
5. Ajouter une photo> Prendre une photo 6. Prenez l'iPad

Résultats attendus:
Viseur de capture d'image S'affiche en plein écran orienté en mode Paysage.

Résultats actuels:
Le viseur de capture d'image pivote de 90 degrés et n'est pas en plein écran.

Versions concernées: iOS 8.0, 8.0.2 et 8.1.

4
Polar Bear

Le problème a été résolu dans iOS 8.1 Testé avec l'application Contacts. fonctionne bien.

Mais un autre problème que j'ai trouvé,

  1. Accédez à l'application Contacts
  2. Gardez votre appareil comme la caméra iPad face au sol.
  3. Ajouter un contact -> Prendre une photo

Testez-le 2 3 fois avec l'orientation FaceUp, la vue de la caméra recommencera à tourner étrangement comme vous pouvez le voir dans les images téléchargées ci-dessus.

3
MaNn

J'ai eu le même problème et après être revenu à l'essentiel et avoir créé un nouveau projet qui a réellement fonctionné .. je l'ai retrouvé;

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

J'espère que ça aide.

1
PaulB

Comme l'a dit @hitme, il s'agit d'un bogue dans iOS 8, mais vous pouvez contourner le problème en définissant -[UIImagePickerController cameraViewTransform] avant de le présenter:

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

Voici un correctif que j'ai trouvé en ajoutant cette catégorie à 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

Je me rends compte qu'il est difficile d'appeler directement les méthodes de cycle de vie UIViewController, mais c'est la seule solution que j'ai trouvée. Espérons Apple corrigera cela bientôt!

0
Rowan Jones

J'ai le même problème. J'ai testé à la fois sur iOS 7.1.2 et iOS 8. Jusqu'à présent, cela ne s'est produit que sur un appareil iPad avec iOS 8. Je le corrige donc temporairement en utilisant le code suivant:

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

Il n'est pas recommandé car il perturbera l'orientation de PresentViewController.

Y a-t-il des réponses de Apple encore?

0
Ray

Impossible d'attendre Apple pour résoudre le problème ...

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

C'est le code que j'ai utilisé pour réparer mon application qui faisait tourner la caméra openCV en paysage si la caméra était démarrée avec l'appareil sur le bureau en position face vers le haut. L'appareil photo devait être forcé en mode portrait pour que l'application fonctionne correctement.

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

Je sais que c'est un bug iOS, mais j'ai implémenté un correctif temporaire:

dans la vue qui présente le sélecteur d'images, ajoutez ce code si vous n'obtenez aucun événement de changement d'orientation (ou utilisez didRotateFromInterfaceOrientation otherwhise):

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

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

}

maintenant en rotation, il suffit de rejeter et de représenter votre imagepicker:

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

fonctionne un peu rugueux, mais c'est la meilleure solution que j'ai trouvée

0
Eliktz