4

Part of my iOS app uses Firebase Storage to store and load images taken/uploaded by the user. However, every time I upload a picture to Firebase in portrait orientation, when I retrieve it back from Firebase later on it comes in landscape orientation and thus displays incorrectly.

I've read that Firebase stores metadata to determine the correct orientation of an uploaded picture but it doesn't seem to be working for me.

Any one know how to get the orientation the image was uploaded in without manually having to rotate it within the app's code?

Thanks!

edit for code:

Uploading:

        let imageMetaData = FIRStorageMetadata()
        imageMetaData.contentType = "image/png"

        var imageData = Data()
        imageData = UIImagePNGRepresentation(chosenImage)!

        let currentUserProfilePictureRef = currentUserStorageRef.child("ProfilePicture")

        currentUserProfilePictureRef.put(imageData, metadata: imageMetaData) { (metaData, error) in
            if error == nil {
                let downloadUrl = metaData?.downloadURL()?.absoluteString
                self.currentUserRef.updateChildValues(["profilePictureUrl": downloadUrl!])
                if (chosenImage.size.width.isLess(than: (chosenImage.size.height))) {
                    self.currentUserRef.updateChildValues(["profilePictureOrientation": "portrait"])
                }
                else {
                    self.currentUserRef.updateChildValues(["profilePictureOrientation": "landscape"])
                }


            } 
        }

Retrieving:

self.currentUserStorageRef.child("ProfilePicture").data(withMaxSize: 20*1024*1024, completion: {(data, error) in
                    var profilePicture = UIImage(data:data!)

                    if(profilePicture?.size.width.isLess(than: (profilePicture?.size.height)!))! {
                        if (pictureOrientation == "landscape") {
                            profilePicture = profilePicture?.rotated(by: Measurement(value: -90.0, unit: .degrees))
                        }
                    } else {
                        if (pictureOrientation == "portrait") {
                            profilePicture = profilePicture?.rotated(by: Measurement(value: 90.0, unit: .degrees))
                        }
                    }

                    self.buttonProfilePicture.setImage(profilePicture, for: .normal)
                    self.buttonProfilePicture.imageView?.contentMode = .scaleAspectFill
                })
AL.
  • 36,815
  • 10
  • 142
  • 281
user3029918
  • 141
  • 1
  • 2
  • 11

3 Answers3

15

From my own personal experience (and asking virtually the same question a year ago), I've found that when you save an image using UIImagePNGRepresentation, it doesn't actually store the orientation data for that specific image. You should use UIImageJPEGRepresentation when you're having orientation issues. JPEG uses the exif format that specifies the orientation for an image, whereas PNG apparently does not. Your issue should be resolved if you save your image using UIImageJPEGRepresentation

Pierce
  • 3,148
  • 16
  • 38
8

Swift 5 - Update

This worked for me, just apply this on your UIImage before uploading it.

extension UIImage {

func fixedOrientation() -> UIImage? {
    guard imageOrientation != UIImage.Orientation.up else {
        // This is default orientation, don't need to do anything
        return self.copy() as? UIImage
    }

    guard let cgImage = self.cgImage else {
        // CGImage is not available
        return nil
    }

    guard let colorSpace = cgImage.colorSpace, let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
        return nil // Not able to create CGContext
    }

    var transform: CGAffineTransform = CGAffineTransform.identity

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

    // Flip image one more time if needed to, this is to prevent flipped image
    switch imageOrientation {
    case .upMirrored, .downMirrored:
        transform = transform.translatedBy(x: size.width, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)
    case .leftMirrored, .rightMirrored:
        transform = transform.translatedBy(x: size.height, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)
    case .up, .down, .left, .right:
        break
    @unknown default:
        break
    }

    ctx.concatenate(transform)

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

    guard let newCGImage = ctx.makeImage() else { return nil }
    return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
}

}

Toto
  • 593
  • 7
  • 20
3

Just an update, UIImageJPEGRepresentation has changed to jpegData in the recent versions of xcode.

jpegData(compressionQuality: 1.0) 1.0 being high quality and 0 being low quality.

T-REX
  • 31
  • 2