Reputation: 1109
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.
Upvotes: 72
Views: 82939
Reputation: 4426
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
Upvotes: 149
Reputation: 131
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()
Upvotes: 2
Reputation: 224
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)
Upvotes: 2
Reputation: 1209
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
}
}
Upvotes: 2
Reputation: 19
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)
}
Upvotes: 0
Reputation: 1949
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
}
Upvotes: 8
Reputation: 1361
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])
Upvotes: 37
Reputation: 1037
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)
}
}
Upvotes: 1
Reputation: 1811
@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!
}
}
Upvotes: 7
Reputation: 8676
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()
Upvotes: 12
Reputation: 11702
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()
}
}
}
Upvotes: 5
Reputation: 33307
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.
Upvotes: 48
Reputation: 123
'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)
Upvotes: 10