検証環境:
Xcode 12
Swift 5.3
今回は縦横の長さが違う画像を 90° 回転させた画像を作成する方法について。
表示上、回転するだけでよい場合は UIView の transform
プロパティを使うと簡単にできる。
let radians = 90 * CGFloat.pi / 180 imageView.transform = imageView.transform.rotated(by: radians) // 90°回転
アニメーション付き
UIViewPropertyAnimator.runningPropertyAnimator( withDuration: 0.3, delay: 0.0, options: [.curveEaseOut]) { self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2) } completion: { position in // do something }
そうではなくて、元の画像から 90° 毎回転させた画像を新たに作成したい場合には ImageContext に書き込んで作成する。
func rotateImage(_ image: UIImage, radians: CGFloat) -> UIImage { let rotatedSize = CGRect(origin: .zero, size: image.size) .applying(CGAffineTransform(rotationAngle: radians)) .integral.size UIGraphicsBeginImageContextWithOptions(rotatedSize, false, image.scale) let context = UIGraphicsGetCurrentContext()! context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2) context.rotate(by: radians) context.scaleBy(x: 1, y: -1) context.translateBy(x: -image.size.width / 2, y: -image.size.height / 2) context.draw(image.cgImage!, in: .init(origin: .zero, size: image.size)) let newImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return newImage }
UIImage の Extension にする場合
extension UIImage { func rotated(by radians: CGFloat) -> UIImage { let rotatedSize = CGRect(origin: .zero, size: size) .applying(CGAffineTransform(rotationAngle: radians)) .integral.size UIGraphicsBeginImageContextWithOptions(rotatedSize, false, scale) if let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage { context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2) context.rotate(by: radians) context.scaleBy(x: 1, y: -1) context.translateBy(x: -size.width / 2, y: -size.height / 2) context.draw(cgImage, in: .init(origin: .zero, size: size)) let rotatedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return rotatedImage ?? self } return self } }
動作確認用のViewControllerサンプル。
class RotateViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() imageView1.contentMode = .scaleAspectFit imageView2.contentMode = .scaleAspectFit imageView1.image = UIImage(named: "coffee")! } var angle: CGFloat = 0 func rotateImage(_ image: UIImage, clockwise: Bool) -> UIImage { var newAngle = clockwise ? angle + 90 : angle - 90 if newAngle <= -360 || newAngle >= 360 { newAngle = 0 } angle = newAngle let radians = angle * CGFloat.pi / 180 let rotatedSize = CGRect(origin: .zero, size: image.size) .applying(CGAffineTransform(rotationAngle: radians)) .integral.size UIGraphicsBeginImageContextWithOptions(rotatedSize, false, image.scale) let context = UIGraphicsGetCurrentContext()! context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2) context.rotate(by: radians) context.scaleBy(x: 1, y: -1) context.translateBy(x: -image.size.width / 2, y: -image.size.height / 2) context.draw(image.cgImage!, in: .init(origin: .zero, size: image.size)) let newImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return newImage } @IBOutlet weak var imageView1: UIImageView! @IBOutlet weak var imageView2: UIImageView! // 反時計回りに -90° 毎回転させる @IBAction func handleLeftButton(_ sender: UIButton) { let newImage = rotateImage(imageView1.image!, clockwise: false) imageView2.image = newImage } // 時計回りに 90° 毎回転させる @IBAction func handleRightButton(_ sender: UIButton) { let newImage = rotateImage(imageView1.image!, clockwise: true) imageView2.image = newImage } }
参考: