How to rotate image in Swift?
Asked Answered
O

15

75

I am unable to rotate the image by 90 degrees in swift. I have written below code but there is an error and doesn't compile

  func imageRotatedByDegrees(oldImage: UIImage, deg 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: oldImage.size.width, height: oldImage.size.height))
    let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat(M_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(M_PI / 180)))

    //Now, draw the rotated/scaled image into the context
    bitmap.scaleBy(x: 1.0, y: -1.0)
    bitmap.draw(oldImage, in:  CGRect(origin: (x: -oldImage.size.width / 2,  y: -oldImage.size.height / 2, width:  oldImage.size.width, height: oldImage.size.height), size: oldImage.cgImage))

    let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return newImage

  }

below is the code i am not sure about

bitmap.draw(oldImage, in:  CGRect(origin: (x: -oldImage.size.width / 2,  y: -oldImage.size.height / 2, width:  oldImage.size.width, height: oldImage.size.height), size: oldImage.cgImage))
Ope answered 30/11, 2016 at 7:20 Comment(3)
Manage the condition for image after capture from camera. And try this link: #9324630 It will help to sort out from the issue.Bertolde
https://mcmap.net/q/92948/-ios-uiimagepickercontroller-result-image-orientation-after-uploadAsaph
When getting errors, never forget to tell people which ones they are!Hairdresser
P
52

This is an extension of UIImage that targets Swift 4.0 and can rotate just the image without the need for a UIImageView. Tested successfully that the image was rotated, and not just had its exif data changed.

import UIKit

extension UIImage {
    func rotate(radians: CGFloat) -> UIImage {
        let rotatedSize = CGRect(origin: .zero, size: size)
            .applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
            .integral.size
        UIGraphicsBeginImageContext(rotatedSize)
        if let context = UIGraphicsGetCurrentContext() {
            let origin = CGPoint(x: rotatedSize.width / 2.0,
                                 y: rotatedSize.height / 2.0)
            context.translateBy(x: origin.x, y: origin.y)
            context.rotate(by: radians)
            draw(in: CGRect(x: -origin.y, y: -origin.x,
                            width: size.width, height: size.height))
            let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            return rotatedImage ?? self
        }

        return self
    }
}

To perform a 180 degree rotation, you can call it like this:

let rotatedImage = image.rotate(radians: .pi)

If for whatever reason it fails to rotate, the original image will then be returned.

Pedrick answered 14/2, 2018 at 6:40 Comment(4)
This works, however the translation/origin seem to be wrong. The rotated image is shifted up and to the left :-/Tumbledown
@Tumbledown Can you post details on how you are calling it and some general details on the image?Pedrick
CodeBender: I'm calling it with let rotatedImage = image.rotate(radians: .pi). The image is 1433 × 2099. I then found another solution which works perfectly: https://mcmap.net/q/270346/-rotating-uiimage-in-swiftTumbledown
As @Tumbledown points out. The accepted solution has several translation issues, The solution that seems to work for everyone is posted in his comment. Here again for your convenience: https://mcmap.net/q/270346/-rotating-uiimage-in-swiftCruel
C
118

set ImageView image

ImageView.transform = ImageView.transform.rotated(by: CGFloat(M_PI_2))

Swift 5

ImageView.transform = ImageView.transform.rotated(by: .pi)        // 180˚

ImageView.transform = ImageView.transform.rotated(by: .pi / 2)    // 90˚

ImageView.transform = ImageView.transform.rotated(by: .pi * 1.5)  // 270˚
Crosseye answered 30/11, 2016 at 7:29 Comment(6)
Why M_PI_4? 90 degrees = M_PI_2Dubose
@JayeshMiruliya, also in Swift 3 you can use CGFloat.pi constant (divided by 2 ofc)Farquhar
For Swift 4 Use: .pi/1 for 180˚ and .pi/2 for 90˚Twitch
To rotate UIImage you may check this GitHub link. Working for Swift3. You may use ".pi/1 for 180˚ and .pi/2 for 90˚" for radians param as per above comment. gist.github.com/norsez/e318d68f6a48786d35b3e7fd3b3a32aePitsaw
For animation - UIView.animate(withDuration: 3) { self.needleImageView.transform = self.needleImageView.transform.rotated(by: CGFloat(Double.pi/2)) }Genovera
perfect, easy, compact!Gestate
P
52

This is an extension of UIImage that targets Swift 4.0 and can rotate just the image without the need for a UIImageView. Tested successfully that the image was rotated, and not just had its exif data changed.

import UIKit

extension UIImage {
    func rotate(radians: CGFloat) -> UIImage {
        let rotatedSize = CGRect(origin: .zero, size: size)
            .applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
            .integral.size
        UIGraphicsBeginImageContext(rotatedSize)
        if let context = UIGraphicsGetCurrentContext() {
            let origin = CGPoint(x: rotatedSize.width / 2.0,
                                 y: rotatedSize.height / 2.0)
            context.translateBy(x: origin.x, y: origin.y)
            context.rotate(by: radians)
            draw(in: CGRect(x: -origin.y, y: -origin.x,
                            width: size.width, height: size.height))
            let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            return rotatedImage ?? self
        }

        return self
    }
}

To perform a 180 degree rotation, you can call it like this:

let rotatedImage = image.rotate(radians: .pi)

If for whatever reason it fails to rotate, the original image will then be returned.

Pedrick answered 14/2, 2018 at 6:40 Comment(4)
This works, however the translation/origin seem to be wrong. The rotated image is shifted up and to the left :-/Tumbledown
@Tumbledown Can you post details on how you are calling it and some general details on the image?Pedrick
CodeBender: I'm calling it with let rotatedImage = image.rotate(radians: .pi). The image is 1433 × 2099. I then found another solution which works perfectly: https://mcmap.net/q/270346/-rotating-uiimage-in-swiftTumbledown
As @Tumbledown points out. The accepted solution has several translation issues, The solution that seems to work for everyone is posted in his comment. Here again for your convenience: https://mcmap.net/q/270346/-rotating-uiimage-in-swiftCruel
P
43

In one line:

rotatedViewBox.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
Pearly answered 21/3, 2018 at 18:57 Comment(2)
This should be the best solutionAlti
Still the best solution in 2023Athenaathenaeum
I
22
 let angle =  CGFloat(M_PI_2)
  let tr = CGAffineTransform.identity.rotated(by: angle)
  ImageView.transform = tr
Illiterate answered 30/11, 2016 at 7:25 Comment(1)
i want to rotate a UIImage not on imageviewOpe
O
17

In Swift 4.2 and Xcode 10.0

dropDownImageView.transform = dropDownImageView.transform.rotated(by: CGFloat(Double.pi / 2))

If you want to add animation...

UIView.animate(withDuration: 2.0, animations: {
   self.dropDownImageView.transform = self.dropDownImageView.transform.rotated(by: CGFloat(Double.pi / 2))
})
Originative answered 5/7, 2018 at 13:6 Comment(0)
U
9

Rotate image via UIGraphics Context is comparatively heavy operation. The problems you may seen loosing quality and memory increase.

I suggest you should try to rotate the layer or view itself.

extension UIView {


    func rotate(degrees: CGFloat) {

        let degreesToRadians: (CGFloat) -> CGFloat = { (degrees: CGFloat) in
            return degrees / 180.0 * CGFloat.pi
        }
        self.transform =  CGAffineTransform(rotationAngle: degreesToRadians(degrees))

        // If you like to use layer you can uncomment the following line 
        //layer.transform = CATransform3DMakeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0)
    }
}

How to use

yourImageView.rotate(degrees: 40)
Unexceptional answered 22/9, 2019 at 5:32 Comment(0)
S
6

You can use the code below for Swift 3:

extension UIImage {

func fixImageOrientation() -> UIImage? {
    var flip:Bool = false //used to see if the image is mirrored
    var isRotatedBy90:Bool = false // used to check whether aspect ratio is to be changed or not

    var transform = CGAffineTransform.identity

    //check current orientation of original image
    switch self.imageOrientation {
    case .down, .downMirrored:
        transform = transform.rotated(by: CGFloat(M_PI));

    case .left, .leftMirrored:
        transform = transform.rotated(by: CGFloat(M_PI_2));
        isRotatedBy90 = true
    case .right, .rightMirrored:
        transform = transform.rotated(by: CGFloat(-M_PI_2));
        isRotatedBy90 = true
    case .up, .upMirrored:
        break
    }

    switch self.imageOrientation {

    case .upMirrored, .downMirrored:
        transform = transform.translatedBy(x: self.size.width, y: 0)
        flip = true

    case .leftMirrored, .rightMirrored:
        transform = transform.translatedBy(x: self.size.height, y: 0)
        flip = true
    default:
        break;
    }

    // calculate the size of the rotated view's containing box for our drawing space
    let rotatedViewBox = UIView(frame: CGRect(origin: CGPoint(x:0, y:0), size: size))
    rotatedViewBox.transform = transform
    let rotatedSize = rotatedViewBox.frame.size

    // Create the bitmap context
    UIGraphicsBeginImageContext(rotatedSize)
    let bitmap = 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.0, y: rotatedSize.height / 2.0);

    // Now, draw the rotated/scaled image into the context
    var yFlip: CGFloat

    if(flip){
        yFlip = CGFloat(-1.0)
    } else {
        yFlip = CGFloat(1.0)
    }

    bitmap!.scaleBy(x: yFlip, y: -1.0)

    //check if we have to fix the aspect ratio
    if isRotatedBy90 {
        bitmap?.draw(self.cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2, width: size.height,height: size.width))
    } else {
        bitmap?.draw(self.cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2, width: size.width,height: size.height))
    }

    let fixedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return fixedImage
}
Swage answered 22/3, 2017 at 6:1 Comment(0)
V
4

Your code is not that far off from functional. You can apply the transform as you are doing directly to the bitmap, you don't need an intermediate view:

func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
    let size = oldImage.size

    UIGraphicsBeginImageContext(size)

    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: size.width / 2, y: size.height / 2)
    //Rotate the image context
    bitmap.rotate(by: (degrees * CGFloat(M_PI / 180)))
    //Now, draw the rotated/scaled image into the context
    bitmap.scaleBy(x: 1.0, y: -1.0)

    let origin = CGPoint(x: -size.width / 2, y: -size.width / 2)

    bitmap.draw(oldImage.cgImage!, in: CGRect(origin: origin, size: size))

    let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return newImage
}

Also, if you're going to create a function that rotates an image, it is typically good form to include a clockwise: Bool parameter that will interpret the degrees argument as rotating clockwise or not. The implementation and appropriate conversion to radians I leave to you.

Also note that it's a bit hand-wavy on my part to assume that oldImage.size is non-zero. If it is, force-unwrapping UIGraphicsGetCurrentContext()! will probably crash. You should validate the oldImage's size and if it's invalid just return oldImage.

Viscid answered 30/11, 2016 at 7:43 Comment(1)
While functional, this code will not rotate the image about the center unless in the unique scenario that the image is square. I used the intermediate method and it produced about-center rotation.Demos
H
2

You can first set your image in imageView and rotate get your image in imageView

Here:

let customImageView = UIImageView()
customImageView.frame = CGRect.init(x: 0, y: 0, w: 250, h: 250)
customImageView.image = UIImage(named: "yourImage")

customImageView .transform = customImageView .transform.rotated(by: CGFloat.init(M_PI_2))
var rotatedImage = UIImage()
rotatedImage = customImageView.image!
Heroism answered 22/9, 2017 at 22:17 Comment(0)
D
0

Your problem is that you use non-existing initializer for CGRect. If you want to use CGRect(origin:size:), then create origin properly, without width and height parameters. Or remove size parameter and use CGRect(x:y:width:height:).

Just replace this line

bitmap.draw(oldImage, in:  CGRect(origin: (x: -oldImage.size.width / 2,  y: -oldImage.size.height / 2, width:  oldImage.size.width, height: oldImage.size.height), size: oldImage.cgImage))

with this one:

let rect = CGRect(origin: CGPoint(x: -oldImage.size.width / 2,  y: -oldImage.size.height / 2), size: oldImage.size)
bitmap.draw(oldImage.cgImage!, in: rect)
Dubose answered 30/11, 2016 at 7:51 Comment(0)
H
0

If you just don't want specific rotation and like to use all orientations one by one.

func rotateImage() -> UIImage {
    let newOrientation = Orientation(rawValue: self.imageOrientation.rawValue + 1) ?? UIImage.Orientation(rawValue: 0)
    return UIImage(cgImage: self.cgImage!, scale: self.scale, orientation: newOrientation!)
}
Hartz answered 16/5, 2020 at 0:49 Comment(0)
B
0
UIGraphicsBeginImageContext(CGSize(width: bitmap.width, height: bitmap.height))
UIImage(cgImage: oldImage.cgImage, scale: 1, orientation: .leftMirrored).draw(at: .zero)
let image = UIGraphicsGetImageFromCurrentImageContext()!

Much shorter.

Bedstead answered 16/6, 2020 at 14:58 Comment(0)
E
0

To start spinning a image view:

func startSpinning()  {
    UIView.animate(withDuration: 0.5, delay: 0, options: .repeat, animations: { () -> Void in
        self.gearImageView!.transform = self.gearImageView!.transform.rotated(by: .pi / 2)
    })
}

To stop spinning:

func stopSpinning()  {
    self.gearImageView.layer.removeAllAnimations()
}
Eluviation answered 3/11, 2021 at 12:11 Comment(0)
P
0

Another working solution is to rotate the UIImage

1. Create an extension for UIImage

extension UIImage {
 
    /// Rotate the UIImage
    /// - Parameter orientation: Define the rotation orientation
    /// - Returns: Get the rotated image 
   func rotateImage(orientation: UIImage.Orientation) -> UIImage {
      guard let cgImage = self.cgImage else { return UIImage() }
      switch orientation {
           case .right:
               return UIImage(cgImage: cgImage, scale: 1.0, orientation: .up)
           case .down:
               return UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)
           case .left:
               return UIImage(cgImage: cgImage, scale: 1.0, orientation: .down)
           default:
               return UIImage(cgImage: cgImage, scale: 1.0, orientation: .left)
       }
   }
}

2. Use it around your code

  • Your UIImage

image.rotateImage(orientation: .up)

Privilege answered 14/6, 2022 at 9:44 Comment(1)
cgImage property might return nil. You should change your return type to optional and return nil. Btw you should use the original image scale property to preserve its scale.Milden
H
-1
ImageView?.transform = transform.rotated(by: (CGFloat(Double.pi / 2)))

Swift 4.2

Rotation - right. Works fine.

Haaf answered 22/11, 2019 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.