76

I'm using Xcode 6.0.1 with Swift. I have a UIImage, and I would like to make another image using the old image as a source, with the new image being rotated in some way... say flipped vertically.

This question was already answered a few months ago. However, that solution doesn't work for me, even though the situation is identical.

When I have

var image = UIImage(CGImage: otherImage.CGImage, scale: 1.0, orientation: .DownMirrored)

Xcode complains that there's an "Extra argument 'scale' in call". After checking with the Apple documentation, this makes no sense, as that version of the initializer does take those three arguments. Leaving out the scale and orientation arguments does fix the problem, but prevents me from doing the rotation.

The only other reference to this that I can find is this guy, who had the same problem.

What do you think?

I do need this to run on this version of Xcode, so if there's an alternate way to perform the rotation (I haven't found one yet) that would be useful.

2
  • 2
    Xcode 6.0.1 is outdated (don't know if that is the problem). Your code compiles without errors or warnings in my Xcode 6.1. Only if you omit the .CGImage (as in the referenced github page) then you'll get a compiler error. Commented Nov 23, 2014 at 18:23
  • You can rotate the image view drawing the image easily if you like in this Q/A stackoverflow.com/questions/40882487/… Commented Feb 6, 2017 at 12:49

14 Answers 14

155

Swift 5 Solution

Here is the simplest solution:

extension UIImage {
    func rotate(radians: Float) -> UIImage? {
        var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
        // Trim off the extremely small float value to prevent core graphics from rounding it up
        newSize.width = floor(newSize.width)
        newSize.height = floor(newSize.height)
        
        UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        
        // Move origin to middle
        context.translateBy(x: newSize.width/2, y: newSize.height/2)
        // Rotate around middle
        context.rotate(by: CGFloat(radians))
        // Draw the image at its center
        self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))
    
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return newImage
    }
}

and to use this solution you can do the following

let image = UIImage(named: "image.png")!
let newImage = image.rotate(radians: .pi/2) // Rotate 90 degrees
Sign up to request clarification or add additional context in comments.

9 Comments

this is a great solution. I used another one which cutted my image after rotation, but this one works perfect.
Be careful when dealing with UIImages with different orientations. self.draw() will automatically draw the image with .up orientation.
I'm using this try to rotate a uiimage in a custom annotationView. the image is a .png of a motorbike. its not rotating the image ... its just returning a black square?
.pi is 180 deg, .pi/2 is 90 deg, .pi/4 is 45 deg, ..., .pi/180 is 1 deg.
If you use SwiftUI, import SwiftUI into your UIImage extension instead of UIKit, and change the function signature to: rotate(angle: Angle). Inside the function, do: let radians = angle.radians. Now you can call the function with: image.rotate(angle: .degrees(90)) or image.rotate(angle: .degrees(-90)
|
48

Here is a simple extension to UIImage:

//ImageRotation.swift

import UIKit

extension UIImage {  
    public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
        let radiansToDegrees: (CGFloat) -> CGFloat = {
            return $0 * (180.0 / CGFloat(M_PI))
        }
        let degreesToRadians: (CGFloat) -> CGFloat = {
            return $0 / 180.0 * CGFloat(M_PI)
        }

        // calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
        let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
        rotatedViewBox.transform = t
        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.
        CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0, rotatedSize.height / 2.0);

        //   // Rotate the image context
        CGContextRotateCTM(bitmap, degreesToRadians(degrees));

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

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

        CGContextScaleCTM(bitmap, yFlip, -1.0)
        CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }
}

(Source)

Use it with:

rotatedPhoto = rotatedPhoto?.imageRotatedByDegrees(90, flip: false) 

The former will rotate an image and flip it if flip is set to true.

9 Comments

confile, flipping works awesome. The problem with the rotation is that is does rotate the image, BUT - it changes the aspect ratio and "destroy it",can you check it and modify the code? it would be great. thanks!
also find it modifies ratio - so close!
UIGraphicsBeginImageContext(rotatedSize) should be UIGraphicsBeginImageContextWithOptions(rotatedSize, false, self.scale) to preserve scale
Xcode gives suggestions to convert this code for Swift 3 except for this line, CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage). Change it to bitmap?.draw( cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2,width: size.width,height: size.height))
This way does not release memory. It keep around 30MB Ram every time I rotate a new photo taken with iPhone 6.
|
37
extension UIImage {
    struct RotationOptions: OptionSet {
        let rawValue: Int

        static let flipOnVerticalAxis = RotationOptions(rawValue: 1)
        static let flipOnHorizontalAxis = RotationOptions(rawValue: 2)
    }

    func rotated(by rotationAngle: Measurement<UnitAngle>, options: RotationOptions = []) -> UIImage? {
        guard let cgImage = self.cgImage else { return nil }

        let rotationInRadians = CGFloat(rotationAngle.converted(to: .radians).value)
        let transform = CGAffineTransform(rotationAngle: rotationInRadians)
        var rect = CGRect(origin: .zero, size: self.size).applying(transform)
        rect.origin = .zero

        let renderer = UIGraphicsImageRenderer(size: rect.size)
        return renderer.image { renderContext in
            renderContext.cgContext.translateBy(x: rect.midX, y: rect.midY)
            renderContext.cgContext.rotate(by: rotationInRadians)

            let x = options.contains(.flipOnVerticalAxis) ? -1.0 : 1.0
            let y = options.contains(.flipOnHorizontalAxis) ? 1.0 : -1.0
            renderContext.cgContext.scaleBy(x: CGFloat(x), y: CGFloat(y))

            let drawRect = CGRect(origin: CGPoint(x: -self.size.width/2, y: -self.size.height/2), size: self.size)
            renderContext.cgContext.draw(cgImage, in: drawRect)
        }
    }
}

You can use it like this:

let rotatedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees))

With flipped flag specified:

let flippedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees), options: [.flipOnVerticalAxis])

6 Comments

Thank you, great solution. Could you may provide also a solution for flipping an image?
@ixany I've updated the answer to flip on the vertical axis. Is that what you meant?
That was exactly what I was looking for. Thank you again! :)
This function has serious issues. When imageOrientation != UIImage.Orientation.up, the function gives a completely wrong image.
in order to maintain the size of the rotated image (which might result in clipped image) remove .applying(transform)
|
12

Swift 3:

Rotating to right:

let image = UIImage(cgImage: otherImage.cgImage!, scale: CGFloat(1.0), orientation: .right)

Tested in Playground:

// MyPlayground.playground

import UIKit
import PlaygroundSupport

let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
view.backgroundColor = UIColor.white
PlaygroundPage.current.liveView = view

let otherImage = UIImage(named: "burger.png", in: Bundle.main,
                         compatibleWith: nil)
let imageViewLeft = UIImageView(image: UIImage(cgImage: (otherImage?.cgImage!)!, scale: CGFloat(1.0), orientation: .left)) 
let imageViewRight = UIImageView(image: UIImage(cgImage: (otherImage?.cgImage!)!, scale: CGFloat(1.0), orientation: .right))

view.addSubview(imageViewLeft)
view.addSubview(imageViewRight)
view.layoutIfNeeded()

3 Comments

Why not? Show me your Playground File!
This does not answer the question, as they were asking for a method to make another image. This solution just uses orientation EXIF data to display the image turned to the right. So, for example, you can't call this twice to rotate the image 180 degrees.
@typewrite: you are right, but it helps others to rotate UIImages
10

'Extra argument in call' generally happens when one of the input types is incorrect.

You can fix your code with

var image = UIImage(CGImage: otherImage.CGImage, scale: CGFloat(1.0), orientation: .DownMirrored)

Comments

8

Do not use UIGraphicsBeginImageContext because it will destroy the quality of your vector, use UIGraphicsBeginImageContextWithOptions. This is my implementation in Swift 3/4:

extension UIImage {

  func rotated(degrees: CGFloat) -> UIImage? {

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

    // Calculate the size of the rotated view's containing box for our drawing space
    let rotatedViewBox: UIView = UIView(frame: CGRect(origin: .zero, size: size))
    rotatedViewBox.transform = CGAffineTransform(rotationAngle: degreesToRadians(degrees))
    let rotatedSize: CGSize = rotatedViewBox.frame.size

    // Create the bitmap context
    UIGraphicsBeginImageContextWithOptions(rotatedSize, false, 0.0)

    guard let bitmap: CGContext = UIGraphicsGetCurrentContext(), let unwrappedCgImage: CGImage = cgImage else {
      return nil
    }

    // 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)

    // Rotate the image context
    bitmap.rotate(by: degreesToRadians(degrees))

    bitmap.scaleBy(x: CGFloat(1.0), y: -1.0)

    let rect: CGRect = CGRect(
        x: -size.width/2,
        y: -size.height/2,
        width: size.width,
        height: size.height)

    bitmap.draw(unwrappedCgImage, in: rect)

    guard let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() else {
      return nil
    }

    UIGraphicsEndImageContext()

    return newImage
  }

2 Comments

How can I use this extension. I try to use it this way: let originalImage = UIImage(named: "image").rotated(degrees: 90) and didn't work.
What didn't work? Didn't it compile or didn't it work as expected? Are you using a vector?
7

@confile answer updated to Swift 4

import UIKit

extension UIImage {

    public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
        let radiansToDegrees: (CGFloat) -> CGFloat = {
            return $0 * (180.0 / CGFloat.pi)
        }
        let degreesToRadians: (CGFloat) -> CGFloat = {
            return $0 / 180.0 * CGFloat.pi
        }

        // calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox = UIView(frame: CGRect(origin: .zero, size: size))
        let t = CGAffineTransform(rotationAngle: degreesToRadians(degrees));
        rotatedViewBox.transform = t
        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)

        //   // Rotate the image context
        bitmap?.rotate(by: degreesToRadians(degrees))

        // 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)
        let rect = CGRect(x: -size.width / 2, y: -size.height / 2, width: size.width, height: size.height)

        bitmap?.draw(cgImage!, in: rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }
}

Comments

5

Try this code, it worked for me:

@IBAction func btnRotateImagePressed(sender: AnyObject) {
    if let originalImage = self.imageView.image {

        let rotateSize = CGSize(width: originalImage.size.height, height: originalImage.size.width)
        UIGraphicsBeginImageContextWithOptions(rotateSize, true, 2.0)
        if let context = UIGraphicsGetCurrentContext() {
            CGContextRotateCTM(context, 90.0 * CGFloat(M_PI) / 180.0)
            CGContextTranslateCTM(context, 0, -originalImage.size.height)
            originalImage.drawInRect(CGRectMake(0, 0, originalImage.size.width, originalImage.size.height))
            self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }
    }
}

3 Comments

I converted this to the Swift4 but it is causing memory problem.
After using UIGraphicsEndImageContext() memory problem is solved.
when capture landscapeLeft image then show image is wrong side.
2

For Swift 4.2

extension UIImage {


func rotate(_ radians: CGFloat) -> UIImage {
    let cgImage = self.cgImage!
    let LARGEST_SIZE = CGFloat(max(self.size.width, self.size.height))
    let context = CGContext.init(data: nil, width:Int(LARGEST_SIZE), height:Int(LARGEST_SIZE), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: cgImage.colorSpace!, bitmapInfo: cgImage.bitmapInfo.rawValue)!

    var drawRect = CGRect.zero
    drawRect.size = self.size
    let drawOrigin = CGPoint(x: (LARGEST_SIZE - self.size.width) * 0.5,y: (LARGEST_SIZE - self.size.height) * 0.5)
    drawRect.origin = drawOrigin
    var tf = CGAffineTransform.identity
    tf = tf.translatedBy(x: LARGEST_SIZE * 0.5, y: LARGEST_SIZE * 0.5)
    tf = tf.rotated(by: CGFloat(radians))
    tf = tf.translatedBy(x: LARGEST_SIZE * -0.5, y: LARGEST_SIZE * -0.5)
    context.concatenate(tf)
    context.draw(cgImage, in: drawRect)
    var rotatedImage = context.makeImage()!

    drawRect = drawRect.applying(tf)

    rotatedImage = rotatedImage.cropping(to: drawRect)!
    let resultImage = UIImage(cgImage: rotatedImage)
    return resultImage
}
}

Comments

2

Tested With Swift 5 Xcode 13

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))
    }

}

Just Pass angle in degree

myView.rotate(degrees : 90)

1 Comment

This rotates a UIView and not a UIImage.
2

If you need to flip the image horizontally or if you need a mirror image of the original image there is a simple way :

This code works for me to flip the image:

let originalImage = UIImage(named: "image")
let flippedImage = originalImage?.withHorizontallyFlippedOrientation()

Documentation

Comments

1

This works for iOS 8+ and maintains the image quality. It's two extensions for UIImage.

  extension UIImage {
    func withSize(_ width: CGFloat, _ height: CGFloat) -> UIImage {

      let target = CGSize(width, height)

      var scaledImageRect = CGRect.zero

      let aspectWidth:CGFloat = target.width / self.size.width
      let aspectHeight:CGFloat = target.height / self.size.height
      let aspectRatio:CGFloat = min(aspectWidth, aspectHeight)

      scaledImageRect.size.width = self.size.width * aspectRatio
      scaledImageRect.size.height = self.size.height * aspectRatio
      scaledImageRect.origin.x = (target.width - scaledImageRect.size.width) / 2.0
      scaledImageRect.origin.y = (target.height - scaledImageRect.size.height) / 2.0

      UIGraphicsBeginImageContextWithOptions(target, false, 0)

      self.draw(in: scaledImageRect)

      let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()

      return scaledImage!
    }

    func rotated(degrees: Double) -> UIImage {

      let radians = CGFloat(Double.pi * degrees / 180)

      var rotatedViewBox: UIView? = UIView(frame: CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale))
      let t = CGAffineTransform(rotationAngle: radians)
      rotatedViewBox!.transform = t
      let rotatedSize = rotatedViewBox!.frame.size
      rotatedViewBox = nil

      // 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, y: rotatedSize.height/2)

      //   // Rotate the image context
      bitmap.rotate(by: radians)

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

      let newImage = UIGraphicsGetImageFromCurrentImageContext()!
      UIGraphicsEndImageContext()

      return newImage.withSize(newImage.size.width/scale, newImage.size.height/scale)
    }
  }

Comments

1

This method provides a rotation of the image while maintaining its visual fidelity, making it ideal for wallpapers, textures in SceneKit, or any scenario where colors and brightness must remain exact.

extension UIImage {
    
    /// Rotates the image by the given radians, preserving original colors and brightness
    func rotated(radians: CGFloat) -> UIImage? {
        guard let cgImage = self.cgImage else { return nil }
        let size = self.size

        // Calculate size of rotated image
        let newWidth = abs(size.width * cos(radians)) + abs(size.height * sin(radians))
        let newHeight = abs(size.width * sin(radians)) + abs(size.height * cos(radians))
        let newSize = CGSize(width: newWidth, height: newHeight)

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

        // Create bitmap context with original color space and bitmap info
        guard let context = CGContext(
            data: nil,
            width: Int(newSize.width),
            height: Int(newSize.height),
            bitsPerComponent: cgImage.bitsPerComponent,
            bytesPerRow: 0,
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
        ) else { return nil }

        // Move origin to center
        context.translateBy(x: newSize.width / 2, y: newSize.height / 2)

        // Rotate context
        context.rotate(by: radians)

        // Draw the original image centered
        context.draw(cgImage, in: CGRect(x: -size.width / 2,
                                         y: -size.height / 2,
                                         width: size.width,
                                         height: size.height))

        // Create new UIImage
        guard let rotatedCGImage = context.makeImage() else { return nil }
        return UIImage(cgImage: rotatedCGImage, scale: self.scale, orientation: .up)
    }
}

1 Comment

stackoverflow.com/questions/53818772/… Hi! Did you manage to solve this problem?
0

Check this:

func rotatedImage(with angle: CGFloat) -> UIImage {

    let updatedSize = CGRect(origin: .zero, size: size)
        .applying(CGAffineTransform(rotationAngle: angle))
        .size

    return UIGraphicsImageRenderer(size: updatedSize)
        .image { _ in

            let context = UIGraphicsGetCurrentContext()

            context?.translateBy(x: updatedSize.width / 2.0, y: updatedSize.height / 2.0)
            context?.rotate(by: angle)

            draw(in: CGRect(x: -size.width / 2.0, y: -size.height / 2.0, width: size.width, height: size.height))
        }
        .withRenderingMode(renderingMode)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.