how could i transform image to look like one from the image ?
just want to reach how that transformation works
for rotating im using an extention of UIImage, but thats all a got for now, just new in swift
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
}
You would need to use Core Animation with CATransform3D, and tweak the transform to give an impression of depth. What you do is to apply a small negative value (-1/200 is a good starting point) to the M34 component of the transformation matrix. You could ether do that to the layer of each of your views, or use a CATransformLayer on a super layer and then add the other images as sublayers of the transform layer.
This is tricky, fussy stuff. I haven't messed with it in a while so I don't remember the fine details any more, but that should be enough to get you started. I suggest searching on "iOS perspective", "M34" and "CATransformLayer" to find out more.
Related
This question already has answers here:
Swift 3 and CGContextDrawImage
(2 answers)
Closed 6 years ago.
I have an UIImage extension that uses CGContextDrawImage.
It simply rotate a UIImage.
I updated to Xcode8 and Swift3 and cant figure out how to use CGContextDrawImage.
I got error on CGContextDrawImage.
"Cannot convert value of type "CGImage.Type to expected argument type 'CGImage?'"
My code is :
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
And whole extension :
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 rectZero = CGPoint(dictionaryRepresentation: CGRect.zero as! CFDictionary)
let rotatedViewBox = UIView(frame: CGRect(origin: rectZero!, 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)
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
In Swift 3, many functions for CGContext are imported as an instance method of CGContext, the property name CGImage has renamed to cgImage. And CGRectMake is removed.
Try this:
bitmap!.draw(cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2, width: size.width, height: size.height))
(Hope ! would be safe for your usage.)
func draw(CGImage, in: CGRect, byTiling: Bool)
I have a problem with swift, I have an app that, when a button is pressed, must rotate an image of 90°, for doing this I use this code
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, 0.5 * rotatedSize.width, 0.5 * rotatedSize.height);
// // 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
}}
with pictures in landscape i haven't any problem but when I try to rotate an image in portrait, on the first press of the button the image is scaled adapting its width to the width of the ImageView, losing his proportions, and without rotate, it is rotated, always with new proportions, only with the second pressure... can anyone help me?
here a screenshot of the starting situation
after first press
after second press
I've created Swift version of this class: https://github.com/bennythemink/ZoomRotatePanImageView/blob/master/ZoomRotatePanImageView.m Works nice. Now I want to save modified image to file. The thing is I want to save it in full resolution and also I want to save area which is only visible to user.
Let me show you simple example:
This is how it looks in my app. Image is one a few samples in iOS simulator. Most of it is out of screen. I want only visible part.
After saving without cropping it looks like this:
So far so good after clipping it'd be nice.
But now let's make some changes:
After saving:
Looks like it's transformed by wrong pivot. How can I fix it?
Here's my code for saving:
UIGraphicsBeginImageContextWithOptions(image.size, false, 0)
let context = UIGraphicsGetCurrentContext()
let transform = imageView.transform
let imageRect = CGRectMake(0, 0, image.size.width, image.size.height)
CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor) //for debugging
CGContextFillRect(context, imageRect)
CGContextConcatCTM(context, transform)
image.drawInRect(imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
There's a simpler method to achieve it:
UIGraphicsBeginImageContextWithOptions(imageContainer.bounds.size, false, 0)
self.imageContainer.drawViewHierarchyInRect(imageContainer.bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
However output image has size of the view not an actual image and I want it in full resolution.
Updated code to work with image of any size :
let boundsScale = imageView.bounds.size.width / imageView.bounds.size.height
let imageScale = image!.size.width / image!.size.height
let size = (image?.size)!
var canvasSize = size
if boundsScale > imageScale {
canvasSize.width = canvasSize.height * boundsScale
}else{
canvasSize.height = canvasSize.width / boundsScale
}
let xScale = canvasSize.width / imageView.bounds.width
let yScale = canvasSize.height / imageView.bounds.height
let center = CGPointApplyAffineTransform(imageView.center, CGAffineTransformScale(CGAffineTransformIdentity, xScale, yScale))
let xCenter = center.x
let yCenter = center.y
UIGraphicsBeginImageContextWithOptions(canvasSize, false, 0);
let context = UIGraphicsGetCurrentContext()!
//Apply transformation
CGContextTranslateCTM(context, xCenter, yCenter)
CGContextConcatCTM(context, imageView.transform)
CGContextTranslateCTM(context, -xCenter, -yCenter)
var drawingRect : CGRect = CGRectZero
drawingRect.size = canvasSize
//Transaltion
drawingRect.origin.x = (xCenter - size.width*0.5)
drawingRect.origin.y = (yCenter - size.height*0.5)
//Aspectfit calculation
if boundsScale > imageScale {
drawingRect.size.width = drawingRect.size.height * imageScale
}else{
drawingRect.size.height = drawingRect.size.width / imageScale
}
image!.drawInRect(drawingRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Simulator screen shot
Saved image
Objective: Get bigger edit image from imagePickerController with correct position.
Question: Could anyone please help me to get the image and tell me what my code is wrong? In my investigation, CGImageCreateWithImageInRect with UIImagePickerControllerCropRect looks weird. But not sure...
Why: info[UIImagePickerControllerEditedImage] is too small, so I want to use info[UIImagePickerControllerEditedImage].
Issue: croppedImage in the code returns wrong position edited image.
Other info: original image is 90 rotated, so I use imageRotatedByDegrees function found in web, and it works well.
class PhotoViewController: UIImagePickerControllerDelegate {
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let rotatedOriginalImage = originalImage!.imageRotatedByDegrees(90, flip: false)
let croppedImage = UIImage(CGImage: CGImageCreateWithImageInRect(rotatedOriginalImage.CGImage, info[UIImagePickerControllerCropRect]!.CGRectValue()))
}
}
// For rotating UIImagePickerControllerOriginalImage
// source: http://blog.ruigomes.me/how-to-rotate-an-uiimage-using-swift/
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
}
}
Found a solution. The problem was look like the image orientation function has a bug.
iOS UIImagePickerController result image orientation after upload
I should read the code more to understand what the root cause is.
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);