84 votes

iOS - UIImageView - comment gérer l'orientation de l'image UIImage ?

Est-il possible de configurer UIImageView pour gérer l'orientation de l'image ? Lorsque je règle le UIImageView à l'image avec orientation DROIT (il s'agit d'une photo du rouleau de l'appareil photo), l'image est tournée vers la droite, mais je veux la montrer dans la bonne orientation, comme elle a été prise.

Je sais que je peux faire pivoter les données d'image mais est-il possible de le faire de manière plus élégante ?

146voto

Tommy Points 56749

Si je comprends bien, ce que vous voulez faire, c'est ignorer l'orientation de l'UIImage ? Si oui, vous pouvez faire ceci :

UIImage *originalImage = [... whatever ...];

UIImage *imageToDisplay =
     [UIImage imageWithCGImage:[originalImage CGImage]
              scale:[originalImage scale]
              orientation: UIImageOrientationUp];

Vous créez donc une nouvelle UIImage avec les mêmes données de pixels que l'original (référencée via sa propriété CGImage) mais vous spécifiez une orientation qui ne fait pas tourner les données.

4 votes

Au fait, comment puis-je faire tourner les données de l'image ?

8 votes

Je suppose que vous devez créer une taille appropriée CGContext avec CGBitmapContextCreate (ou avec le UIGraphicsBeginImageContext ), utilisez CGContextRotateCTM pour définir une rotation, utilisez soit drawInRect: sur le UIImage ou CGContextDrawImage avec l'image CGImage puis convertir le contexte en image soit avec la propriété UIGraphicsGetImageFromCurrentImageContext (et, ensuite, UIGraphicsEndImageContext ) si vous avez utilisé UIKit pour créer le contexte, ou CGBitmapContextCreateImage si vous vous en teniez au Core Graphics. UIKit n'est pas très thread safe, mais le code serait plus propre.

0 votes

Je n'arrive pas à faire fonctionner cette méthode lorsque je place l'image dans une ImageView... elle affiche l'image avec son orientation originale même si je crée l'image avec une orientation miroir : [UIImage imageWithCGImage:image.CGImage scale:image.scale orientation:UIImageOrientationUpMirrored]... L'OP a demandé d'utiliser une ImageView mais je n'arrive pas à faire fonctionner cette solution dans une ImageView...

53voto

user2067021 Points 735

Vous pouvez complètement éviter de faire manuellement les transformations et la mise à l'échelle vous-même, comme suggéré par an0 dans cette réponse ici :

- (UIImage *)normalizedImage {
    if (self.imageOrientation == UIImageOrientationUp) return self; 

    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    [self drawInRect:(CGRect){0, 0, self.size}];
    UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return normalizedImage;
}

La documentation des méthodes UIImage taille et drawInRect indique explicitement qu'ils tiennent compte de l'orientation.

2 votes

C'est une bouée de sauvetage.

0 votes

Où dois-je le coller ?

27voto

varender singh Points 207

Cette méthode vérifie d'abord l'orientation actuelle de l'UIImage, puis elle change l'orientation dans le sens des aiguilles d'une montre et renvoie l'UIImage.

self.imageView.image = rotateImage(currentUIImage)

   func rotateImage(image:UIImage)->UIImage
    {
        var rotatedImage = UIImage();
        switch image.imageOrientation
        {
            case UIImageOrientation.Right:
            rotatedImage = UIImage(CGImage:image.CGImage!, scale: 1, orientation:UIImageOrientation.Down);

           case UIImageOrientation.Down:
            rotatedImage = UIImage(CGImage:image.CGImage!, scale: 1, orientation:UIImageOrientation.Left);

            case UIImageOrientation.Left:
            rotatedImage = UIImage(CGImage:image.CGImage!, scale: 1, orientation:UIImageOrientation.Up);

             default:
            rotatedImage = UIImage(CGImage:image.CGImage!, scale: 1, orientation:UIImageOrientation.Right);
        }
        return rotatedImage;
    }

Version Swift 4

func rotateImage(image:UIImage) -> UIImage
    {
        var rotatedImage = UIImage()
        switch image.imageOrientation
        {
        case .right:
            rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .down)

        case .down:
            rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .left)

        case .left:
            rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .up)

        default:
            rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .right)
        }

        return rotatedImage
    }

0 votes

Bonjour Varender, et merci pour votre aide sur SO. Pourriez-vous développer un peu sur la façon dont vous pensez que cela pourrait aider Martin ? Comment plus élégant que la rotation des données est votre solution ?

0 votes

1. nous prendrons l'UIImage courante de l'UIImageView 2. nous appellerons cette méthode (rotateImage) en passant cette UIImage courante comme argument 3. nous stockerons la valeur retournée dans UIImageView.image /* ceci fera tourner les données dans la vue image :) :)

0 votes

Monsieur, selon moi, c'est la meilleure réponse car si nous faisons pivoter l'UIImageView entière, l'UIImageView X aura une valeur négative. Si vous prenez une UIImageView carrée, vous pouvez simplement transformer toute l'UIImageView en utilisant CGAffineTransformMakeRotate.

26voto

Aqua Points 409

Swift 3.1

func fixImageOrientation(_ image: UIImage)->UIImage {
    UIGraphicsBeginImageContext(image.size)
    image.draw(at: .zero)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage ?? image
}

1 votes

C'est une solution parfaite :)

0 votes

Tu mérites tous les morceaux mon ami

0 votes

C'est mieux que que . Cela fonctionne dans swift 4, mais pas dans ce cas.

25voto

NicolasMiari Points 2687

J'ai converti le code dans La réponse d'Anomie ici (copié-collé ci-dessus par suvish valsan) en Swift :

func fixOrientation() -> UIImage {
    if self.imageOrientation == UIImageOrientation.Up {
        return self
    }

    var transform = CGAffineTransformIdentity

    switch self.imageOrientation {
    case .Down, .DownMirrored:
        transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI));

    case .Left, .LeftMirrored:
        transform = CGAffineTransformTranslate(transform, self.size.width, 0);
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2));

    case .Right, .RightMirrored:
        transform = CGAffineTransformTranslate(transform, 0, self.size.height);
        transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2));

    case .Up, .UpMirrored:
        break
    }

    switch self.imageOrientation {

    case .UpMirrored, .DownMirrored:
        transform = CGAffineTransformTranslate(transform, self.size.width, 0)
        transform = CGAffineTransformScale(transform, -1, 1)

    case .LeftMirrored, .RightMirrored:
        transform = CGAffineTransformTranslate(transform, self.size.height, 0)
        transform = CGAffineTransformScale(transform, -1, 1);

    default:
        break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    let ctx = CGBitmapContextCreate(
        nil,
        Int(self.size.width),
        Int(self.size.height),
        CGImageGetBitsPerComponent(self.CGImage),
        0,
        CGImageGetColorSpace(self.CGImage),
        UInt32(CGImageGetBitmapInfo(self.CGImage).rawValue)
    )

    CGContextConcatCTM(ctx, transform);

    switch self.imageOrientation {
    case .Left, .LeftMirrored, .Right, .RightMirrored:
        // Grr...
        CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.height,self.size.width), self.CGImage);

    default:
        CGContextDrawImage(ctx, CGRectMake(0, 0, self.size.width,self.size.height), self.CGImage);
        break;
    }

    // And now we just create a new UIImage from the drawing context
    let cgimg = CGBitmapContextCreateImage(ctx)

    let img = UIImage(CGImage: cgimg!)

    return img;
}

(J'ai remplacé toutes les occurrences du paramètre image avec self car mon code est une extension de UIImage ).


EDIT : Version Swift 3.

La méthode renvoie une option, car de nombreux appels intermédiaires peuvent échouer et je n'aime pas utiliser la méthode ! .

func fixOrientation() -> UIImage? {

    guard let cgImage = self.cgImage else {
        return nil
    }

    if self.imageOrientation == UIImageOrientation.up {
        return self
    }

    let width  = self.size.width
    let height = self.size.height

    var transform = CGAffineTransform.identity

    switch self.imageOrientation {
    case .down, .downMirrored:
        transform = transform.translatedBy(x: width, y: height)
        transform = transform.rotated(by: CGFloat.pi)

    case .left, .leftMirrored:
        transform = transform.translatedBy(x: width, y: 0)
        transform = transform.rotated(by: 0.5*CGFloat.pi)

    case .right, .rightMirrored:
        transform = transform.translatedBy(x: 0, y: height)
        transform = transform.rotated(by: -0.5*CGFloat.pi)

    case .up, .upMirrored:
        break
    }

    switch self.imageOrientation {
    case .upMirrored, .downMirrored:
        transform = transform.translatedBy(x: width, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)

    case .leftMirrored, .rightMirrored:
        transform = transform.translatedBy(x: height, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)

    default:
        break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    guard let colorSpace = cgImage.colorSpace else {
        return nil
    }

    guard let context = CGContext(
        data: nil,
        width: Int(width),
        height: Int(height),
        bitsPerComponent: cgImage.bitsPerComponent,
        bytesPerRow: 0,
        space: colorSpace,
        bitmapInfo: UInt32(cgImage.bitmapInfo.rawValue)
        ) else {
            return nil
    }

    context.concatenate(transform);

    switch self.imageOrientation {

    case .left, .leftMirrored, .right, .rightMirrored:
        // Grr...
        context.draw(cgImage, in: CGRect(x: 0, y: 0, width: height, height: width))

    default:
        context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
    }

    // And now we just create a new UIImage from the drawing context
    guard let newCGImg = context.makeImage() else {
        return nil
    }

    let img = UIImage(cgImage: newCGImg)

    return img;
}

(Note : La version odes de Swift 3 compile sous Xcode 8.1, mais je n'ai pas testé qu'elle fonctionne réellement. Il peut y avoir une faute de frappe quelque part, un mélange de largeur/hauteur, etc. N'hésitez pas à signaler/réparer toute erreur).

0 votes

J'ai utilisé le code ci-dessus après l'avoir converti en swift 3. Vérifiez ma réponse ci-dessous si quelqu'un a besoin de la version 3 de swift.

0 votes

L'orientation de l'image n'a pas changé du tout. Désolé, je suis nouveau dans le développement iOS. Ce que j'ai fait, c'est que j'ai utilisé la fonction pour l'extension UIImage, puis je l'ai définie par programme à l'intérieur de func viewDidLoad . Cette image provient d'un téléphone Samsung photos.google.com/share/ et possède des données exif d'orientation 270 CW. Voici comment je l'utilise let background: UIImage? = UIImage(named: "background_image")?.fixOrientation() backgroundImage.image = background

0 votes

Il y a des problèmes de fuites de mémoire avec cette application @Nicolas ? on dirait que les CGImages ne sont pas désallouées. Mon application est une application de gestion de photos de masse et traite de nombreuses photos. La fonction semble fuir la mémoire à chaque fois qu'elle est exécutée. Est-ce qu'il y a des solutions rapides à ce problème ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X