27

In my iOS application I'm writing, I deal with PNGs because I deal with the alpha channel. For some reason, I can load a PNG into my imageView just fine, but when it comes time to either copy the image out of my application (onto the PasteBoard) or save the image to my camera roll, the image rotates 90 degrees.

I've searched everywhere on this, and one of the things I learned is that if I used JPEGs, I wouldn't have this problem (it sounds), due to the EXIF information.

My app has full copy/paste functionality, and here's the kicker (I'll write this in steps so it is easier to follow):

  1. Go to my camera roll and copy an image
  2. Go into my app and press "Paste", image pastes just fine, and I can do that all day
  3. Click the copy function I implemented, and then click "Paste", and the image pastes but is rotated.

I am 100% sure my copy and paste code isn't what is wrong here, because if I go back to Step 2 above, and click "save", the photo saves to my library but it is rotated 90 degrees!

What is even more strange is that it seems to work fine with images downloaded from the internet, but is very hit or miss with images I manually took with the phone. Some it works, some it doesn't...

Does anybody have any thoughts on this? Any possible work arounds I can use? I'm pretty confident in the code being it works for about 75% of my images. I can post the code upon request though.

Boeckm
  • 3,264
  • 4
  • 36
  • 41
  • 2
    Try displaying the [imageOrientation](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIImage_Class/Reference/Reference.html#//apple_ref/occ/instp/UIImage/imageOrientation) property of your images. Do the ones that appear rotated have a value other than `UIImageOrientationUp`? – Cowirrie Apr 24 '12 at 23:59
  • PNG doesn't have a very defined standard for the EXIF metadata but it does include metadata information itself, which is probably why the orientation in that metadata is giving you the undesirable results. – Pochi Apr 25 '12 at 00:39
  • Just threw NSLogs in the Copy, Paste, Save, and Take Photo methods... Good news is that all methods are consistent, bad news, the images that have this weird rotate problem have an imageOrientation of Right (90 deg clock wise). Pretty much what I figured, question is how to fix this? I wouldn't mind just blowing that attribute away if reasonable. – Boeckm Apr 25 '12 at 00:54
  • Hmmm... Just tested something, if I throw my phone into airplane mode, and use the "Take photo" feature of my app, it works fine. Is this due to fact that when in airplane mode, the phone can't use geotagging, and therefore doesn't write EXIF metadata to the image?? – Boeckm Apr 25 '12 at 00:55
  • possible duplicate of [iOS UIImagePickerController result image orientation after upload](http://stackoverflow.com/questions/5427656/ios-uiimagepickercontroller-result-image-orientation-after-upload) – Axel Guilmin Apr 03 '15 at 00:13

6 Answers6

26

For those that want a Swift solution, create an extension of UIImage and add the following method:

func correctlyOrientedImage() -> UIImage {
    if self.imageOrientation == .up {
        return self
    }

    UIGraphicsBeginImageContextWithOptions(size, false, scale)
    draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    let normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return normalizedImage ?? self;
}
Josh Heald
  • 3,907
  • 28
  • 37
Tom J
  • 261
  • 3
  • 4
14

If you're having trouble due to the existing image imageOrientation property, you can construct an otherwise identical image with different orientation like this:

CGImageRef imageRef = [sourceImage CGImage];

UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];

You may need to experiment with just what orientation to set on your replacement images, possibly switching based on the orientation you started with.

Also keep an eye on your memory usage. Photography apps often run out, and this will double your storage per picture, until you release the source image.

Cowirrie
  • 7,218
  • 1
  • 29
  • 42
  • I can give this a shot tonight, good call with the memory usage problem too. I still don't understand why the imageOrientation property is being set to "3" (90 deg CW) on all images taken with my iPhone?! If possible, I'd like to just force the orientation to UP on all images. – Boeckm Apr 25 '12 at 11:11
  • The downfall to this is that I need to have this code everywhere. I'll need to add this code in my paste method, in my "select image" method, and in my "take image" method. Granted, I could do something smart about it, I'm still not really a fan of this. – Boeckm Apr 25 '12 at 16:44
  • This doesn't work either. In my "paste" method, I set imageRef = to the pasteboard.image, and then I rotate the image using the second line of code you provided, and set my image view to that image, but it still doesn't work... – Boeckm Apr 25 '12 at 21:36
  • In my example, I hardcoded to `UIImageOrientationUp`. You'll probably need to experiment with what new orientation to use, given certain existing orientations. – Cowirrie Apr 25 '12 at 21:46
13

Took a few days, but I finally figured it out thanks to the answer @Dondragmer posted. But I figured I'd post my full solution.

So basically I had to write a method to intelligently auto-rotate my images. The downside is that I have to call this method everywhere throughout my code and it is kind of processor intensive, especially when working on mobile devices, but the plus side is that I can take images, copy images, paste images, and save images and they all rotate properly. Here's the code I ended up using (the method isn't 100% complete yet, still need to edit memory leaks and what not).

I ended up learning that the very first time an image was insert into my application (whether that be due to a user pressing "take image", "paste image", or "select image", for some reason it insert just fine without auto rotating. At this point, I stored whatever the rotation value was in a global variable called imageOrientationWhenAddedToScreen. This made my life easier because when it came time to manipulate the image and save the image out of the program, I simply checked this cached global variable and determined if I needed to properly rotate the image.

    - (UIImage*) rotateImageAppropriately:(UIImage*) imageToRotate {
    //This method will properly rotate our image, we need to make sure that
    //We call this method everywhere pretty much...

    CGImageRef imageRef = [imageToRotate CGImage];
    UIImage* properlyRotatedImage;

    if (imageOrientationWhenAddedToScreen == 0) {
       //Don't rotate the image
        properlyRotatedImage = imageToRotate;

    } else if (imageOrientationWhenAddedToScreen == 3) {

        //We need to rotate the image back to a 3
        properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:3];

    } else if (imageOrientationWhenAddedToScreen == 1) {

        //We need to rotate the image back to a 1
        properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:1];        

    }

    return properlyRotatedImage;

}

I am still not 100% sure why Apple has this weird image rotation behavior (try this... Take your phone and turn it upside down and take a picture, you'll notice that the final picture turns out right side up - perhaps this is why Apple has this type of functionality?).

I know I spent a great deal of time figuring this out, so I hope it helps other people!

Boeckm
  • 3,264
  • 4
  • 36
  • 41
13

This "weird rotation" behavior is really not that weird at all. It is smart, and by smart I mean memory efficient. When you rotate an iOS device the camera hardware rotates with it. When you take a picture that picture will be captured however the camera is oriented. The UIImage is able to use this raw picture data without copying by just keeping track of the orientation it should be in. When you use UIImagePNGRepresentation() you lose this orientation data and get a PNG of the underlying image as it was taken by the camera. To fix this instead of rotating you can tell the original image to draw itself to a new context and get the properly oriented UIImage from that context.

UIImage *image = ...;

//Have the image draw itself in the correct orientation if necessary
if(!(image.imageOrientation == UIImageOrientationUp ||
    image.imageOrientation == UIImageOrientationUpMirrored))
{
    CGSize imgsize = image.size;
    UIGraphicsBeginImageContext(imgsize);
    [image drawInRect:CGRectMake(0.0, 0.0, imgsize.width, imgsize.height)];
    image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}

NSData *png = UIImagePNGRepresentation(image);
Joe
  • 56,979
  • 9
  • 128
  • 135
0

Here is one more way to achieve that:

@IBAction func rightRotateAction(sender: AnyObject) {

    let imgToRotate = CIImage(CGImage: sourceImageView.image?.CGImage)
    let transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
    let rotatedImage = imgToRotate.imageByApplyingTransform(transform)
    let extent = rotatedImage.extent()
    let contex = CIContext(options: [kCIContextUseSoftwareRenderer: false])
    let cgImage = contex.createCGImage(rotatedImage, fromRect: extent)
    adjustedImage = UIImage(CGImage: cgImage)!
    UIView.transitionWithView(sourceImageView, duration: 0.5, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
            self.sourceImageView.image = self.adjustedImage
        }, completion: nil)
}
Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
0

You can use Image I/O to save PNG image to file(or NSMutableData) with respect to the orientation of the image. In the example below I save the PNG image to a file at path.

- (BOOL)savePngFile:(UIImage *)image toPath:(NSString *)path {
    NSData *data = UIImagePNGRepresentation(image);
    int exifOrientation = [UIImage cc_iOSOrientationToExifOrientation:image.imageOrientation];
    NSDictionary *metadata = @{(__bridge id)kCGImagePropertyOrientation:@(exifOrientation)};
    NSURL *url = [NSURL fileURLWithPath:path];
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    if (!source) {
        return NO;
    }
    CFStringRef UTI = CGImageSourceGetType(source);
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)url, UTI, 1, NULL);
    if (!destination) {
        CFRelease(source);
        return NO;
    }
    CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metadata);
    BOOL success = CGImageDestinationFinalize(destination);
    CFRelease(destination);
    CFRelease(source);
    return success;
}

cc_iOSOrientationToExifOrientation: is a method of UIImage category.

+ (int)cc_iOSOrientationToExifOrientation:(UIImageOrientation)iOSOrientation {
    int exifOrientation = -1;
    switch (iOSOrientation) {
        case UIImageOrientationUp:
            exifOrientation = 1;
            break;

        case UIImageOrientationDown:
            exifOrientation = 3;
            break;

        case UIImageOrientationLeft:
            exifOrientation = 8;
            break;

        case UIImageOrientationRight:
            exifOrientation = 6;
            break;

        case UIImageOrientationUpMirrored:
            exifOrientation = 2;
            break;

        case UIImageOrientationDownMirrored:
            exifOrientation = 4;
            break;

        case UIImageOrientationLeftMirrored:
            exifOrientation = 5;
            break;

        case UIImageOrientationRightMirrored:
            exifOrientation = 7;
            break;

        default:
            exifOrientation = -1;
    }
    return exifOrientation;
}

You can alternatively save the image to NSData using CGImageDestinationCreateWithData and pass NSMutableData instead of NSURL in CGImageDestinationCreateWithURL.

KudoCC
  • 6,912
  • 1
  • 24
  • 53