ios 在 Swift 中旋转 UIImage
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27092354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rotating UIImage in Swift
提问by std_answ
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.
我在 Swift 中使用 Xcode 6.0.1。我有一个 UIImage,我想使用旧图像作为源制作另一个图像,新图像以某种方式旋转......比如说垂直翻转。
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.
Xcode 抱怨有一个“调用中的额外参数‘scale’”。在查看 Apple 文档后,这是没有意义的,因为该版本的初始化程序确实采用了这三个参数。省略比例和方向参数确实可以解决问题,但会阻止我进行旋转。
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.
我确实需要它来在这个版本的 Xcode 上运行,所以如果有一种替代方法来执行旋转(我还没有找到)那会很有用。
回答by Josh Bernfeld
Swift 5 Solution
Swift 5 解决方案
I wrote Pixel SDKwhich offers a more powerful solution to this problem, otherwise here is the simplest solution:
我编写了Pixel SDK,它为这个问题提供了更强大的解决方案,否则这里是最简单的解决方案:
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
}
}
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
回答by confile
Here is a simple extension to UIImage:
这是 UIImage 的一个简单扩展:
//ImageRotation.swift
import UIKit
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return rotatedPhoto = rotatedPhoto?.imageRotatedByDegrees(90, flip: false)
* (180.0 / CGFloat(M_PI))
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return 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)
}
}
}
/ 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:
使用它:
let rotatedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees))
The former will rotate an image and flip it if flip is set to true.
如果翻转设置为真,前者将旋转图像并翻转它。
回答by Stefan Church
let flippedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees), options: [.flipOnVerticalAxis])
You can use it like this:
你可以这样使用它:
let image = UIImage(cgImage: otherImage.cgImage!, scale: CGFloat(1.0), orientation: .right)
With flipped flag specified:
指定翻转标志:
// 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()
回答by Peter Kreinz
Swift 3:
斯威夫特 3:
Rotating to right:
向右旋转:
var image = UIImage(CGImage: otherImage.CGImage, scale: CGFloat(1.0), orientation: .DownMirrored)
Tested in Playground:
在操场上测试:
@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()
}
}
}
回答by Dan
'Extra argument in call' generally happens when one of the input types is incorrect.
当输入类型之一不正确时,通常会发生“调用中的额外参数”。
You can fix your code with
你可以修复你的代码
import UIKit
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return 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
}
* (180.0 / CGFloat.pi)
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return 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
}
}
/ 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!
}
}
回答by Khuong
Try this code, it worked for me:
试试这个代码,它对我有用:
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)
}
}
回答by Oscar
@confile answer updated to Swift 4
@confile 答案更新到 Swift 4
##代码##回答by Pedro Paulo Amorim
Do not use UIGraphicsBeginImageContext
because it will destroy the quality of your vector, use UIGraphicsBeginImageContextWithOptions
. This is my implementation in Swift 3/4:
不要使用,UIGraphicsBeginImageContext
因为它会破坏您的载体的质量,请使用UIGraphicsBeginImageContextWithOptions
. 这是我在 Swift 3/4 中的实现:
回答by xuzepei
For Swift 4.2
对于 Swift 4.2
##代码##回答by Everton Cunha
This works for iOS 8+ and maintains the image quality. It's two extensions for UIImage.
这适用于 iOS 8+ 并保持图像质量。它是 UIImage 的两个扩展。
##代码##