How Can I Rotate A UIImage and Image Data By 90 Degrees - ios

I have a UIImageView that contains an image. At the minute the user can click to save the image within the UIImageView to disk.
I would like to make it so that the the user can click to rotate the UIImageView and also rotate the UImage within the view so that when the image is saved it keeps the new rotation.
At the minute I have
- (IBAction)rotateImage:(id)sender {
float degrees = 90; //the value in degrees
self.imagePreview.transform = CGAffineTransformMakeRotation(degrees * M_PI/180);
}
Can someone point me in the right direction regarding keeping the current rotation.
Thanks

I have such code in my old app. But this code can be simplified because you have no need to rotate it on non-90 degrees angle.
Objective-C
#implementation UIImage (Rotation)
- (UIImage *)imageRotatedOnDegrees:(CGFloat)degrees
{
// Follow ing code can only rotate images on 90, 180, 270.. degrees.
CGFloat roundedDegrees = (CGFloat)(round(degrees / 90.0) * 90.0);
BOOL sameOrientationType = ((NSInteger)roundedDegrees) % 180 == 0;
CGFloat radians = M_PI * roundedDegrees / 180.0;
CGSize newSize = sameOrientationType ? self.size : CGSizeMake(self.size.height, self.size.width);
UIGraphicsBeginImageContext(newSize);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGImageRef cgImage = self.CGImage;
if (ctx == NULL || cgImage == NULL) {
UIGraphicsEndImageContext();
return self;
}
CGContextTranslateCTM(ctx, newSize.width / 2.0, newSize.height / 2.0);
CGContextRotateCTM(ctx, radians);
CGContextScaleCTM(ctx, 1, -1);
CGPoint origin = CGPointMake(-(self.size.width / 2.0), -(self.size.height / 2.0));
CGRect rect = CGRectZero;
rect.origin = origin;
rect.size = self.size;
CGContextDrawImage(ctx, rect, cgImage);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image ?: self;
}
#end
Swift
extension UIImage {
func imageRotated(on degrees: CGFloat) -> UIImage {
// Following code can only rotate images on 90, 180, 270.. degrees.
let degrees = round(degrees / 90) * 90
let sameOrientationType = Int(degrees) % 180 == 0
let radians = CGFloat.pi * degrees / CGFloat(180)
let newSize = sameOrientationType ? size : CGSize(width: size.height, height: size.width)
UIGraphicsBeginImageContext(newSize)
defer {
UIGraphicsEndImageContext()
}
guard let ctx = UIGraphicsGetCurrentContext(), let cgImage = cgImage else {
return self
}
ctx.translateBy(x: newSize.width / 2, y: newSize.height / 2)
ctx.rotate(by: radians)
ctx.scaleBy(x: 1, y: -1)
let origin = CGPoint(x: -(size.width / 2), y: -(size.height / 2))
let rect = CGRect(origin: origin, size: size)
ctx.draw(cgImage, in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image ?? self
}
}

This will rotate an image by any given radians.
Note this works 2x and 3x retina as well
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees {
CGFloat radians = DegreesToRadians(degrees);
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(radians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2);
CGContextRotateCTM(bitmap, radians);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2 , self.size.width, self.size.height), self.CGImage );
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

In Swift 4
imageView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi/2))

The highlighted state of a UIButton did not take the correct orientation of the normal inserted turned image. So I had to redraw the image like #RyanG showed. Here is the Swift 2.2 code for that:
extension UIImage
{
/// Rotate an image by any given radians.
/// Works for 2x and 3x retina as well.
func imageRotatedByDegrees(degrees: Double) -> UIImage
{
let radians = CGFloat(degrees * (M_PI / 180.0))
let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: self.size))
let t: CGAffineTransform = CGAffineTransformMakeRotation(radians)
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
UIGraphicsBeginImageContextWithOptions(rotatedSize, false, self.scale)
let bitmap = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2)
CGContextRotateCTM(bitmap, radians)
CGContextScaleCTM(bitmap, 1.0, -1.0)
CGContextDrawImage(bitmap, CGRect(origin: CGPoint(x: -self.size.width / 2, y: -self.size.height / 2), size: self.size), self.CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}

Swift version made from answer of #RyanG with some fixes:
func rotated(by degrees: CGFloat) -> UIImage {
let radians : CGFloat = degrees * CGFloat(.pi / 180.0)
let rotatedViewBox = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
let t = CGAffineTransform(rotationAngle: radians)
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
UIGraphicsBeginImageContextWithOptions(rotatedSize, false, self.scale)
defer { UIGraphicsEndImageContext() }
guard let bitmap = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage else {
return self
}
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
bitmap.rotate(by: radians)
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(cgImage, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else {
return self
}
return newImage
}

self.imageview.transform = CGAffineTransformMakeRotation(M_PI/2);

Related

Swift how to crop image with always 1:1 aspect ratio

I am using this library's cropping functions to crop image like Instagram does. (https://github.com/fahidattique55/FAImageCropper) And its cropping part of the code works like this.
private func captureVisibleRect() -> UIImage {
var croprect = CGRect.zero
let xOffset = (scrollView.imageToDisplay?.size.width)! / scrollView.contentSize.width;
let yOffset = (scrollView.imageToDisplay?.size.height)! / scrollView.contentSize.height;
croprect.origin.x = scrollView.contentOffset.x * xOffset;
croprect.origin.y = scrollView.contentOffset.y * yOffset;
let normalizedWidth = (scrollView?.frame.width)! / (scrollView?.contentSize.width)!
let normalizedHeight = (scrollView?.frame.height)! / (scrollView?.contentSize.height)!
croprect.size.width = scrollView.imageToDisplay!.size.width * normalizedWidth
croprect.size.height = scrollView.imageToDisplay!.size.height * normalizedHeight
let toCropImage = scrollView.imageView.image?.fixImageOrientation()
let cr: CGImage? = toCropImage?.cgImage?.cropping(to: croprect)
let cropped = UIImage(cgImage: cr!)
return cropped }
But the problem is for example i have a photo with (800(W)*600(H)) size, and i want to crop it with full width by using full zoom out.This function calculates croprect variable (800(W)*800(H)) correctly. But after this part of the code let cr: CGImage? = toCropImage?.cgImage?.cropping(to: croprect) the cr's resolution becomes (800(W)*600(H)). How can i transform this to square image by filling the empty parts of it with white color?
You can square the image after this process by using the answer in this link. How to draw full UIImage inside a square with white color on the edge
This is the Swift 3 version of it.
private func squareImageFromImage(image: UIImage) -> UIImage{
var maxSize = max(image.size.width,image.size.height)
var squareSize = CGSize.init(width: maxSize, height: maxSize)
var dx = (maxSize - image.size.width) / 2.0
var dy = (maxSize - image.size.height) / 2.0
UIGraphicsBeginImageContext(squareSize)
var rect = CGRect.init(x: 0, y: 0, width: maxSize, height: maxSize)
var context = UIGraphicsGetCurrentContext()
context?.setFillColor(UIColor.white.cgColor)
context?.fill(rect)
rect = rect.insetBy(dx: dx, dy: dy)
image.draw(in: rect, blendMode: CGBlendMode.normal, alpha: 1.0)
var squareImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return squareImage!
}
I suggest you use UIGraphicsContext to draw a rectangle with the intended width and height, filling it with the desired color. Then draw the cropped image on it.
I haven't tested this but this should work for what you want.
I have omitted other parts of your code to focus on the essentials.
....
let context: CGContext? = UIGraphicsGetCurrentContext()
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let color = UIColor.white
color.setFill()
context?.fill(rect)
let cr: CGImage? = toCropImage?.cgImage?.cropping(to: croprect)
let cropped = UIImage(cgImage: cr!)
context?.draw(cropped, in: rect)
let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
Replace width and height with the desired width and height.
Simple extensions for Cropping images in different ways
I you want to crop from the center use cropAspectFill and if you want to keep full image and want to make it square then use cropAspectFit
Objective-C solution
#implementation UIImage (crop)
- (UIImage *)cropAspectFill {
CGFloat minSize = MIN(self.size.height, self.size.width);
CGSize squareSize = CGSizeMake(minSize, minSize);
// Get our offset to center the image inside our new square frame
CGFloat dx = (minSize - self.size.width) / 2.0f;
CGFloat dy = (minSize - self.size.height) / 2.0f;
UIGraphicsBeginImageContext(squareSize);
CGRect rect = CGRectMake(0, 0, minSize, minSize);
// Adjust the rect to be centered in our new image
rect = CGRectInset(rect, dx, dy);
[self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return squareImage;
}
- (UIImage *)cropAspectFit {
// Get a square that the image will fit into
CGFloat maxSize = MIN(self.size.height, self.size.width);
CGSize squareSize = CGSizeMake(maxSize, maxSize);
// Get our offset to center the image inside our new square frame
CGFloat dx = (maxSize - self.size.width) / 2.0f;
CGFloat dy = (maxSize - self.size.height) / 2.0f;
UIGraphicsBeginImageContext(squareSize);
CGRect rect = CGRectMake(0, 0, maxSize, maxSize);
// Adjust the rect to be centered in our new image
rect = CGRectInset(rect, dx, dy);
[self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return squareImage;
}
#end
Swift solution
extension UIImage {
func cropAspectFill() -> UIImage {
let minSize = min(size.height, size.width)
let squareSize = CGSize(width: minSize, height: minSize)
// Get our offset to center the image inside our new square frame
let dx = (minSize - size.width) / 2.0
let dy = (minSize - size.height) / 2.0
UIGraphicsBeginImageContext(squareSize)
let rect = CGRect(x: 0, y: 0, width: minSize, height: minSize)
// Adjust the rect to be centered in our new image
let centeredRect = rect.insetBy(dx: dx, dy: dy)
draw(in: centeredRect, blendMode: .normal, alpha: 1.0)
let squareImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return squareImage!
}
func cropAspectFit() -> UIImage {
// Get a square that the image will fit into
let maxSize = min(size.height, size.width)
let squareSize = CGSize(width: maxSize, height: maxSize)
// Get our offset to center the image inside our new square frame
let dx = (maxSize - size.width) / 2.0
let dy = (maxSize - size.height) / 2.0
UIGraphicsBeginImageContext(squareSize)
let rect = CGRect(x: 0, y: 0, width: maxSize, height: maxSize)
// Adjust the rect to be centered in our new image
let centeredRect = rect.insetBy(dx: dx, dy: dy)
draw(in: centeredRect, blendMode: .normal, alpha: 1.0)
let squareImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return squareImage!
}
}

Rotate UIImage in swift

I want to rotate UIImage in clockwise direction. But my current code do not perform function accurate some time it rotate and some time its skip rotation. I want my UIImage will continuously rotate clockwise on my action button.
Here is my current code:
imageView.image = imageView.image!.imageRotatedByDegrees(angle, flip: false)
angle = angle + 90
if angle > 360{
angle = 0
}
This works for me:
works Fine :)
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: CGRectMake(0, 0, oldImage.size.width, oldImage.size.height))
let t: CGAffineTransform = CGAffineTransformMakeRotation(degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContextRef = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2)
//Rotate the image context
CGContextRotateCTM(bitmap, (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-oldImage.size.width / 2, -oldImage.size.height / 2, oldImage.size.width, oldImage.size.height), oldImage.CGImage)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Swift 3:
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.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.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
extension UIImage {
public func imageRotatedByDegrees(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: self.size.width, height: self.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.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.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
public func fixedOrientation() -> UIImage {
if imageOrientation == UIImageOrientation.up {
return self
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch imageOrientation {
case UIImageOrientation.down, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
break
case UIImageOrientation.left, UIImageOrientation.leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi/2)
break
case UIImageOrientation.right, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -CGFloat.pi/2)
break
case UIImageOrientation.up, UIImageOrientation.upMirrored:
break
}
switch imageOrientation {
case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
transform.translatedBy(x: size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
break
case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
transform.translatedBy(x: size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:
break
}
let ctx: CGContext = CGContext(data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: self.cgImage!.bitsPerComponent,
bytesPerRow: 0,
space: self.cgImage!.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
ctx.concatenate(transform)
switch imageOrientation {
case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
break
}
let cgImage: CGImage = ctx.makeImage()!
return UIImage(cgImage: cgImage)
}
}
#Jason, You should fix orientation of image before rotation.
let image = oldimage.fixedOrientation().imageRotatedByDegrees(90.0)
#Jason Chitla, I cant comment yet, so Im posting it as a new answer. The above solution from #Albert works, but only for square images. The problem is, that
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
is still using the original size, so if you rotate it 90 degrees, aspect is wrong.
What works for me is to use the new rotatedSize, like this:
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(self.cgImage!, in: CGRect(x: -rotatedSize.width / 2, y: -rotatedSize.height / 2, width: rotatedSize.width, height: rotatedSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
You can create a function like this to make your image rotate in clockwise forever:
override func viewDidLoad() {
super.viewDidLoad()
let kRotationAnimationKey = "com.myapplication.rotationanimationkey"
func rotateView(view: UIView, duration: Double = 1) {
if view.layer.animationForKey(kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
}
}
rotateView(self.imageView)
}
Result is:
For those who need the code from #Mughees in Swift 3:
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.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
I fixed first function of #gleb's answer little bit. Size calculation is not necessary there. We simply should do it with reverse.
Because if you do it size of 1149 x 356 with #gleb's function then you will get image sized 357 x 1149.
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
let rotatedSize: CGSize = CGRect(x: 0, y: 0, width: self.size.height, height: self.size.width).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.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
This s probably easier and it is Swift 5 syntax:
// MARK: Rotate
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)
let context = UIGraphicsGetCurrentContext()!
// 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
}
}
I believe it's worth to post solution using CoreImage. It's just couple of lines and it works great for me.
Please note: when getting final UIImage, it's necessary to convert to CGImage first to respect extent of CIImage
extension UIImage {
func imageRotated(by degrees: CGFloat) -> UIImage {
let orientation = CGImagePropertyOrientation(imageOrientation)
// Create CIImage respecting image's orientation
guard let inputImage = CIImage(image: self)?.oriented(orientation)
else { return self }
// Rotate the image itself
let rotation = CGAffineTransform(rotationAngle: (degrees * CGFloat.pi / 180))
let outputImage = inputImage.transformed(by: rotation)
// Create CGImage first
guard let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent)
else { return self }
// Create output UIImage from CGImage
return UIImage(cgImage: cgImage)
}
}

Resize an image with drawInRect while maintaining the aspect ratio like Scale Aspect Fill?

I would like to resize an image with drawInRect method, but I would also like to maintain the right aspect ratio, while filling completely the given frame (as .ScaleAspectFill does for UIViewContentMode).
Anyone has a ready answer for this?
Here is my code (pretty straightforward...):
func scaled100Image() -> UIImage {
let newSize = CGSize(width: 100, height: 100)
UIGraphicsBeginImageContext(newSize)
self.pictures[0].drawInRect(CGRect(x: 0, y: 0, width: 100, height: 100))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
OK, so no ready-made answer... I wrote a swift extension for UIImage, feel free to use it if you need it.
Here it is:
extension UIImage {
func drawInRectAspectFill(rect: CGRect) {
let targetSize = rect.size
if targetSize == .zero {
self.draw(in: rect)
}
let widthRatio = targetSize.width / self.size.width
let heightRatio = targetSize.height / self.size.height
let scalingFactor = max(widthRatio, heightRatio)
let newSize = CGSize(width: self.size.width * scalingFactor,
height: self.size.height * scalingFactor)
UIGraphicsBeginImageContext(targetSize)
let origin = CGPoint(x: (targetSize.width - newSize.width) / 2,
y: (targetSize.height - newSize.height) / 2)
self.draw(in: CGRect(origin: origin, size: newSize))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
scaledImage?.draw(in: rect)
}
}
So in the example above, you use it like that:
self.pictures[0].drawInRectAspectFill(CGRect(x: 0, y: 0, width: 100, height: 100))
The Objective-C version, if someone need it(Paste this code inside a UIIMage category):
- (void) drawInRectAspectFill:(CGRect) recto {
CGSize targetSize = recto.size;
if (targetSize.width <= CGSizeZero.width && targetSize.height <= CGSizeZero.height ) {
return [self drawInRect:recto];
}
float widthRatio = targetSize.width / self.size.width;
float heightRatio = targetSize.height / self.size.height;
float scalingFactor = fmax(widthRatio, heightRatio);
CGSize newSize = CGSizeMake(self.size.width * scalingFactor, self.size.height * scalingFactor);
UIGraphicsBeginImageContext(targetSize);
CGPoint origin = CGPointMake((targetSize.width-newSize.width)/2,(targetSize.height - newSize.height) / 2);
[self drawInRect:CGRectMake(origin.x, origin.y, newSize.width, newSize.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[scaledImage drawInRect:recto];
}

Draw border around content of UIImageView

I've a UIImageView with a image with is a car with a transparent background:
And I want to draw a border around the car:
How can I reach this effect?
At the moment, I've tested CoreGraphics in this way, but without good results:
// load the image
UIImage *img = carImage;
UIGraphicsBeginImageContext(img.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor] setFill];
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, img.size.width * 1.1, img.size.height*1.1);
CGContextDrawImage(context, rect, img.CGImage);
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Any help? Thanks.
Here's what I did:
I did it in Swift just to check it in playgrounds, think you can translate it to Objective-C easily:
import UIKit
func drawOutlie(#image:UIImage, color:UIColor) -> UIImage
{
var newImageKoef:CGFloat = 1.08
var outlinedImageRect = CGRect(x: 0.0, y: 0.0, width: image.size.width * newImageKoef, height: image.size.height * newImageKoef)
var imageRect = CGRect(x: image.size.width * (newImageKoef - 1) * 0.5, y: image.size.height * (newImageKoef - 1) * 0.5, width: image.size.width, height: image.size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, newImageKoef)
image.drawInRect(outlinedImageRect)
var context = UIGraphicsGetCurrentContext()
CGContextSetBlendMode(context, kCGBlendModeSourceIn)
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, outlinedImageRect)
image.drawInRect(imageRect)
var newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
var imageIn = UIImage(named: "158jM")
var imageOut = drawOutlie(image: imageIn, UIColor.redColor())
So how does it work?
We create clean context (aka canvas) with a bit bigger size then original image (for outline)
We draw our image on whole canvas
We fill that image with color
We draw smaller image on top
You can change outline size changing this property : var newImageKoef:CGFloat = 1.08
Here's a result that I had in playgrounds
Swift 5:
extension UIImage {
func drawOutline(imageKeof: CGFloat = 0.2, color: UIColor = .white)-> UIImage? {
let outlinedImageRect = CGRect(x: 0.0, y: 0.0, width: size.width * imageKeof, height: size.height * imageKeof)
let imageRect = CGRect(x: self.size.width * (imageKeof - 1) * 0.5, y: self.size.height * (imageKeof - 1) * 0.5, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, imageKeof)
draw(in: outlinedImageRect)
let context = UIGraphicsGetCurrentContext()
context!.setBlendMode(.sourceIn)
context!.setFillColor(color.cgColor)
context!.fill(outlinedImageRect)
draw(in: imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
haawa answer for Swift 5
extension UIImage {
func drawOutlie(imageKeof: CGFloat = 1.01, color: UIColor) -> UIImage? {
let outlinedImageRect = CGRect(x: 0.0, y: 0.0,
width: size.width * imageKeof,
height: size.height * imageKeof)
let imageRect = CGRect(x: size.width * (imageKeof - 1) * 0.5,
y: size.height * (imageKeof - 1) * 0.5,
width: size.width,
height: size.height)
UIGraphicsBeginImageContextWithOptions(outlinedImageRect.size, false, imageKeof)
draw(in: outlinedImageRect)
guard let context = UIGraphicsGetCurrentContext() else {return nil}
context.setBlendMode(.sourceIn)
context.setFillColor(color.cgColor)
context.fill(outlinedImageRect)
draw(in: imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
This approach is a little bit different but more simple.
imageView.layer.shadowColor = UIColor.red.cgColor
imageView.layer.shadowOffset = CGSize(width: 0, height: 0)
imageView.layer.shadowOpacity = 1
imageView.layer.shadowRadius = 10.0
imageView.clipsToBounds = false

Add transparent space around a UIImage

Lets say we have an image of 600X400 pixel and we want to end up with an new image of 1000x1000 pixel which contains the initial image in the centre and transparent space around it. How can I achieve that in code?
In Swift you can write an extension to UIImage that draws image with insets around it.
Swift 3:
import UIKit
extension UIImage {
func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: self.size.width + insets.left + insets.right,
height: self.size.height + insets.top + insets.bottom), false, self.scale)
let _ = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
OLD ANSWER:
import UIKit
extension UIImage {
func imageWithInsets(insets: UIEdgeInsets) -> UIImage {
UIGraphicsBeginImageContextWithOptions(
CGSizeMake(self.size.width + insets.left + insets.right,
self.size.height + insets.top + insets.bottom), false, self.scale)
let context = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.drawAtPoint(origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
This is the solution in Swift 4 inspired by DrummerB answer:
import UIKit
extension UIImage {
func addImagePadding(x: CGFloat, y: CGFloat) -> UIImage? {
let width: CGFloat = size.width + x
let height: CGFloat = size.height + y
UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0)
let origin: CGPoint = CGPoint(x: (width - size.width) / 2, y: (height - size.height) / 2)
draw(at: origin)
let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithPadding
}
}
How to apply:
let image = UIImage(named: "your-image")!
let imageView = UIImageView(image: image.addImagePadding(x: 50, y: 50))
imageView.backgroundColor = UIColor.gray
view.addSubview(imageView)
Features:
Simply pass padding values via parameters
Colored padding (by setting the UIGraphicsBeginImageContextWithOptions opaque parameter to false)
You create a new image context that is 1000x1000, draw your old image in the middle, then get the new UIImage from the context.
// Setup a new context with the correct size
CGFloat width = 1000;
CGFloat height = 1000;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f,
(height - oldImage.size.height) / 2.0f);
[oldImage drawAtPoint:origin];
// Clean up and get the new image.
UIGraphicsPopContext();
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
A fix for appsunited's answer with better naming convension. To not confuse it the function is mutating or not:
extension UIImage {
func withPadding(_ padding: CGFloat) -> UIImage? {
return withPadding(x: padding, y: padding)
}
func withPadding(x: CGFloat, y: CGFloat) -> UIImage? {
let newWidth = size.width + 2 * x
let newHeight = size.height + 2 * y
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
let origin = CGPoint(x: (newWidth - size.width) / 2, y: (newHeight - size.height) / 2)
draw(at: origin)
let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithPadding
}
}
Make a category on UIImage and try this:
+ (UIImage *)imageWithInsets:(CGRect)insetRect image:(UIImage *)image {
CGRect newRect = CGRectMake(0.0, 0.0, insetRect.origin.x+insetRect.size.width+image.size.width, insetRect.origin.y+insetRect.size.height+image.size.height);
// Setup a new context with the correct size
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake(insetRect.origin.x, insetRect.origin.y);
[image drawAtPoint:origin];
// Clean up and get the new image.
UIGraphicsPopContext();
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Resources