67

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.

Community
  • 1
  • 1
std_answ
  • 1,039
  • 1
  • 11
  • 17
  • 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. – Martin R Nov 23 '14 at 18:23
  • You can rotate the image view drawing the image easily if you like in this Q/A http://stackoverflow.com/questions/40882487/how-to-rotate-image-in-swift-3#40882640 – Khaled Annajar Feb 06 '17 at 12:49

13 Answers13

143

Swift 5 Solution

I wrote Pixel SDK which offers a more powerful solution to this problem, otherwise 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
Josh Bernfeld
  • 4,246
  • 2
  • 32
  • 35
  • 4
    this is a great solution. I used another one which cutted my image after rotation, but this one works perfect. – Adela Toderici Feb 15 '18 at 19:47
  • 2
    Be careful when dealing with UIImages with different orientations. self.draw() will automatically draw the image with .up orientation. – crom87 May 13 '18 at 11:00
  • 2
    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? – lozflan Jul 30 '18 at 23:48
  • 9
    `.pi` is 180 deg, `.pi/2` is 90 deg, `.pi/4` is 45 deg, ..., `.pi/180` is 1 deg. – lenooh Oct 27 '18 at 16:08
  • 1
    Good answer. you saving my day!! – oddK Jun 19 '19 at 02:28
  • 2
    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)` – P. Ent Mar 06 '20 at 13:47
  • Beware of the force unwrap on `UIGraphicsGetCurrentContext()`, it can be nil and hence cause your app to crash (happened to me, rarely, but still). – vomi Apr 11 '23 at 17:27
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.

Michael
  • 32,527
  • 49
  • 210
  • 370
  • 4
    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! – Roi Mulia Jul 25 '15 at 01:53
  • also find it modifies ratio - so close! – Jonathan Plackett Dec 15 '15 at 16:17
  • `ImageView.tintColor` stops working after rotating the image with this function. Even if I try to reset `imageWithRenderingMode` to `AlwaysTemplate`. Any idea why? – maganap Jun 13 '16 at 07:11
  • 2
    `UIGraphicsBeginImageContext(rotatedSize) ` should be `UIGraphicsBeginImageContextWithOptions(rotatedSize, false, self.scale)` to preserve scale – adrum Jul 11 '16 at 22:05
  • @adrum, that was perfect! Mine was pixelating, but was fixed after using your replacement suggestion! – kbpontius Jul 26 '16 at 16:45
  • Works for me, a nice copy and paste solution. – chengsam Aug 03 '16 at 06:38
  • 7
    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))` – Hanny Oct 03 '16 at 06:21
  • Got an error `CGBitmapContextCreate: invalid data bytes/row: should be at least 7680 for 8 integer bits/component, 3 components, kCGImageAlphaPremultipliedFirst.` Anyone knew this error? – Kevin Lieser Dec 10 '16 at 09:32
  • 1
    This way does not release memory. It keep around 30MB Ram every time I rotate a new photo taken with iPhone 6. – t4nhpt Dec 29 '16 at 09:56
36
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])
Stefan Church
  • 1,351
  • 1
  • 10
  • 14
11

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()
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
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)
Dan
  • 123
  • 5
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!
    }
}
Oscar Falmer
  • 1,771
  • 1
  • 24
  • 38
7

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
  }
Pedro Paulo Amorim
  • 1,838
  • 2
  • 27
  • 50
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()
        }
    }
}
Twitter khuong291
  • 11,328
  • 15
  • 80
  • 116
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
}
}
DeyaEldeen
  • 10,847
  • 10
  • 42
  • 75
xuzepei
  • 1,119
  • 11
  • 9
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

Mudith Chathuranga Silva
  • 7,253
  • 2
  • 50
  • 58
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)
Nabeel ali
  • 196
  • 1
  • 5
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)
    }
  }
Everton Cunha
  • 1,017
  • 8
  • 10
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)
}
Shadow
  • 19