简体   繁体   English

iOS - UIImageView - 如何处理 UIImage 图像方向

[英]iOS - UIImageView - how to handle UIImage image orientation

Is it possible to setup UIImageView to handle image orientation?是否可以设置UIImageView来处理图像方向? When I set the UIImageView to image with orientation RIGHT (it is photo from camera roll), the image is rotated to right, but I want to show it in proper orientation, as it was taken.当我将UIImageView设置为方向为RIGHT 的图像(它是来自相机胶卷的照片)时,图像会向右旋转,但我想以正确的方向显示它,因为它是拍摄的。

I know I can rotate image data but it is possible to do it more elegant?我知道我可以旋转图像数据,但有可能做得更优雅吗?

If I understand, what you want to do is disregard the orientation of the UIImage?如果我明白,你想做的是无视 UIImage 的方向? If so then you could do this:如果是这样,那么你可以这样做:

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

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

So you're creating a new UIImage with the same pixel data as the original (referenced via its CGImage property) but you're specifying an orientation that doesn't rotate the data.因此,您正在创建一个具有与原始像素数据相同的像素数据的新 UIImage(通过其 CGImage 属性引用),但您指定的方向不会旋转数据。

You can completely avoid manually doing the transforms and scaling yourself, as suggested by an0 in this answer here :您可以完全避免手工做的转换和缩放自己,在这个答案AN0的建议在这里

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

The documentation for the UIImage methods size and drawInRect explicitly states that they take into account orientation. UIImage 方法sizedrawInRect的文档明确指出它们考虑了方向。

This method first checks the current orientation of UIImage and then it changes the orientation in a clockwise way and return UIImage.You can show this image as此方法首先检查 UIImage 的当前方向,然后以顺时针方式更改方向并返回 UIImage。您可以将此图像显示为

self.imageView.image = rotateImage(currentUIImage) 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;
    }

Swift 4 version斯威夫特 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
    }

I converted the code in Anomie's answer here (copy-pasted above by suvish valsan) into Swift :我将Anomie 的答案中的代码(由 suvish valsan 在上面复制粘贴)转换为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;
}

(I replaced all occurencies of the parameter image with self , because my code is an extension on UIImage ). (我用self替换了参数image所有出现,因为我的代码是UIImage的扩展)。


EDIT: Swift 3 version.编辑: Swift 3 版本。

The method returns an optional, because many of the intermediate calls can fail and I don't like to use !该方法返回一个可选的,因为许多中间调用可能会失败,我不喜欢使用! . .

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: Swift 3 version odes compile under Xcode 8.1, but haven't tested it actually works. There might be a typo somewhere, mixed up width/height, etc. Feel free to point/fix any errors). (注意:Swift 3 版本的 odes 在 Xcode 8.1 下编译,但尚未测试它是否实际有效。某处可能有错字,宽度/高度混淆等。请随时指出/修复任何错误)。

Swift 3.1斯威夫特 3.1

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

UIImage extension in Swift. Swift 中的 UIImage 扩展。 You don't need to do all that flipping at all, really.你根本不需要做所有的翻转,真的。 Objective-C original is here , but I've added the bit that respects the alpha of the original image (crudely, but it works to differentiate opaque images from transparent images). Objective-C original is here ,但我添加了尊重原始图像 alpha 的位(粗略地说,但它可以区分不透明图像和透明图像)。

// from https://github.com/mbcharbonneau/UIImage-Categories/blob/master/UIImage%2BAlpha.m
// Returns true if the image has an alpha layer
    private func hasAlpha() -> Bool {
        guard let cg = self.cgImage else { return false }
        let alpha = cg.alphaInfo
        let retVal = (alpha == .first || alpha == .last || alpha == .premultipliedFirst || alpha == .premultipliedLast)
        return retVal
    }

    func normalizedImage() -> UIImage? {
        if self.imageOrientation == .up {
            return self
        }
        UIGraphicsBeginImageContextWithOptions(self.size, !self.hasAlpha(), self.scale)
        var rect = CGRect.zero
        rect.size = self.size
        self.draw(in: rect)
        let retVal = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return retVal
    }

here is a workable sample cod, considering the image orientation:这是一个可行的示例代码,考虑到图像方向:

#define rad(angle) ((angle) / 180.0 * M_PI)
- (CGAffineTransform)orientationTransformedRectOfImage:(UIImage *)img
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };

    return CGAffineTransformScale(rectTransform, img.scale, img.scale);
}


- (UIImage *)croppedImage:(UIImage*)orignialImage InRect:(CGRect)visibleRect{
    //transform visible rect to image orientation
    CGAffineTransform rectTransform = [self orientationTransformedRectOfImage:orignialImage];
    visibleRect = CGRectApplyAffineTransform(visibleRect, rectTransform);

    //crop image
    CGImageRef imageRef = CGImageCreateWithImageInRect([orignialImage CGImage], visibleRect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:orignialImage.scale orientation:orignialImage.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

I converted the code from @Nicolas Miari answer to Swift 3 in case anybody needs it我将@Nicolas Miari 答案中的代码转换为 Swift 3,以防有人需要它

func fixOrientation() -> UIImage
{

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

    var transform = CGAffineTransform.identity

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

    case .left, .leftMirrored:
        transform = transform.translatedBy(x: self.size.width, y: 0);
        transform = transform.rotated(by: CGFloat(M_PI_2));

    case .right, .rightMirrored:
        transform = transform.translatedBy(x: 0, y: self.size.height);
        transform = transform.rotated(by: CGFloat(-M_PI_2));

    case .up, .upMirrored:
        break
    }


    switch self.imageOrientation {

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

    case .leftMirrored, .rightMirrored:
        transform = transform.translatedBy(x: self.size.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.
    let ctx = CGContext(
        data: nil,
        width: Int(self.size.width),
        height: Int(self.size.height),
        bitsPerComponent: self.cgImage!.bitsPerComponent,
        bytesPerRow: 0,
        space: self.cgImage!.colorSpace!,
        bitmapInfo: UInt32(self.cgImage!.bitmapInfo.rawValue)
    )



    ctx!.concatenate(transform);

    switch self.imageOrientation {

    case .left, .leftMirrored, .right, .rightMirrored:
        // Grr...
        ctx?.draw(self.cgImage!, in: CGRect(x:0 ,y: 0 ,width: self.size.height ,height:self.size.width))

    default:
        ctx?.draw(self.cgImage!, in: CGRect(x:0 ,y: 0 ,width: self.size.width ,height:self.size.height))
        break;
    }

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

    let img = UIImage(cgImage: cgimg!)

    return img;

}

Thanks to Waseem05 for his Swift 3 translation but his method only worked for me when I wrapped it inside an extension to UIImage and placed it outside/below the parent class like so:感谢 Waseem05 的 Swift 3 翻译,但他的方法仅在我将它包装在 UIImage 的扩展中并将其放置在父类的外部/下方时才对我有用,如下所示:

extension UIImage {

        func fixOrientation() -> UIImage
        {

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

        var transform = CGAffineTransform.identity

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

        case .left, .leftMirrored:
            transform = transform.translatedBy(x: self.size.width, y: 0);
            transform = transform.rotated(by: CGFloat(M_PI_2));

        case .right, .rightMirrored:
            transform = transform.translatedBy(x: 0, y: self.size.height);
            transform = transform.rotated(by: CGFloat(-M_PI_2));

        case .up, .upMirrored:
            break
        }


        switch self.imageOrientation {

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

        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: self.size.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.
        let ctx = CGContext(
            data: nil,
            width: Int(self.size.width),
            height: Int(self.size.height),
            bitsPerComponent: self.cgImage!.bitsPerComponent,
            bytesPerRow: 0,
            space: self.cgImage!.colorSpace!,
            bitmapInfo: UInt32(self.cgImage!.bitmapInfo.rawValue)
        )



        ctx!.concatenate(transform);

        switch self.imageOrientation {

        case .left, .leftMirrored, .right, .rightMirrored:
            // Grr...
            ctx?.draw(self.cgImage!, in: CGRect(x:0 ,y: 0 ,width: self.size.height ,height:self.size.width))

        default:
            ctx?.draw(self.cgImage!, in: CGRect(x:0 ,y: 0 ,width: self.size.width ,height:self.size.height))
            break;
        }

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

        let img = UIImage(cgImage: cgimg!)

        return img;

    }
}

Then called it with:然后调用它:

let correctedImage:UIImage = wonkyImage.fixOrientation()

And all was then well!然后一切都很好! Apple should make it easier to discard orientation when we don't need front/back camera and up/down/left/right device orientation metadata.当我们不需要前置/后置摄像头和上/下/左/右设备方向元数据时,Apple 应该更容易丢弃方向。

If you need to rotate and fix the image orientation below extension would be useful.如果您需要旋转和固定扩展下方的图像方向会很有用。

extension UIImage {

    public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
        //Calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
        let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
        rotatedViewBox.transform = t
        let rotatedSize: CGSize = rotatedViewBox.frame.size
        //Create the bitmap context
        UIGraphicsBeginImageContext(rotatedSize)
        let bitmap: CGContext = UIGraphicsGetCurrentContext()!
        //Move the origin to the middle of the image so we will rotate and scale around the center.
        bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
        //Rotate the image context
        bitmap.rotate(by: (degrees * CGFloat.pi / 180))
        //Now, draw the rotated/scaled image into the context
        bitmap.scaleBy(x: 1.0, y: -1.0)
        bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }


    public func fixedOrientation() -> UIImage {
        if imageOrientation == UIImageOrientation.up {
            return self
        }

        var transform: CGAffineTransform = CGAffineTransform.identity

        switch imageOrientation {
        case UIImageOrientation.down, UIImageOrientation.downMirrored:
            transform = transform.translatedBy(x: size.width, y: size.height)
            transform = transform.rotated(by: CGFloat.pi)
            break
        case UIImageOrientation.left, UIImageOrientation.leftMirrored:
            transform = transform.translatedBy(x: size.width, y: 0)
            transform = transform.rotated(by: CGFloat.pi/2)
            break
        case UIImageOrientation.right, UIImageOrientation.rightMirrored:
            transform = transform.translatedBy(x: 0, y: size.height)
            transform = transform.rotated(by: -CGFloat.pi/2)
            break
        case UIImageOrientation.up, UIImageOrientation.upMirrored:
            break
        }

        switch imageOrientation {
        case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
            transform.translatedBy(x: size.width, y: 0)
            transform.scaledBy(x: -1, y: 1)
            break
        case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
            transform.translatedBy(x: size.height, y: 0)
            transform.scaledBy(x: -1, y: 1)
        case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:
            break
        }

        let ctx: CGContext = CGContext(data: nil,
                                       width: Int(size.width),
                                       height: Int(size.height),
                                       bitsPerComponent: self.cgImage!.bitsPerComponent,
                                       bytesPerRow: 0,
                                       space: self.cgImage!.colorSpace!,
                                       bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!

        ctx.concatenate(transform)

        switch imageOrientation {
        case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
            ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
        default:
            ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
            break
        }

        let cgImage: CGImage = ctx.makeImage()!

        return UIImage(cgImage: cgImage)
    }
}
extension UIImage {
    func fixImageOrientation() -> UIImage {
        UIGraphicsBeginImageContext(self.size)
        self.draw(at: .zero)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage ?? self
    }
}
  1. Create extension like top example.创建像顶级示例一样的扩展。
  2. Call it: imageView.image?.fixImageOrientation() or UIImage(named: "someImage").fixImageOrientation()调用它: imageView.image?.fixImageOrientation()UIImage(named: "someImage").fixImageOrientation()

  3. Good luck all!祝大家好运!

Swift 3.0 版汤米的回答

let imageToDisplay = UIImage.init(cgImage: originalImage.cgImage!, scale: originalImage.scale, orientation: UIImageOrientation.up)

Inspired from @Aqua Answer.....灵感来自@Aqua 答案......

in Objective C在目标 C

- (UIImage *)fixImageOrientation:(UIImage *)img {

   UIGraphicsBeginImageContext(img.size);
   [img drawAtPoint:CGPointZero];

   UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   if (newImg) {
       return newImg;
   }

   return img;
}

I tried the checkmarked solution and it doesn't work for me.我尝试了选中标记的解决方案,但它对我不起作用。 But the below solution works for fixing orientation from camera before converting to png.但是下面的解决方案适用于在转换为 png 之前从相机固定方向。

Swift 5:斯威夫特 5:

let imageToDisplay = originalImage.resize(new: originalImage.resize)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM