在Swift中旋转UIImage

时间:2014-11-23 18:02:14

标签: ios swift uiimage

我正在使用Xcode 6.0.1和Swift。我有一个UIImage,我想用旧图像作为源制作另一个图像,新图像以某种方式旋转......比如垂直翻转。

这个问题已经回答a few months ago。但是,即使情况相同,该解决方案对我也不起作用。

当我有

var image = UIImage(CGImage: otherImage.CGImage, scale: 1.0, orientation: .DownMirrored)

Xcode抱怨在调用中有一个“额外参数'缩放'”。在使用Apple文档检查之后,这没有任何意义,因为该版本的初始化程序确实采用了这三个参数。省略比例和方向参数可以解决问题,但是阻止我进行轮换。

我能找到的唯一其他参考资料是this guy,他们遇到了同样的问题。

您怎么看?

我确实需要在这个版本的Xcode上运行,所以如果有另一种方法来执行旋转(我还没有找到一个),这将是有用的。

12 个答案:

答案 0 :(得分:46)

这是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

将其用于:

rotatedPhoto = rotatedPhoto?.imageRotatedByDegrees(90, flip: false) 

如果将flip设置为true,前者将旋转图像并翻转它。

答案 1 :(得分:40)

Swift 5解决方案

这是最简单的解决方案

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
    }
}

并使用此解决方案,您可以执行以下操作

let image = UIImage(named: "image.png")!
let newImage = image.rotate(radians: .pi/2) // Rotate 90 degrees

答案 2 :(得分:27)

$ awk '{array[NR][1]; split($0,array[NR],";"); for(i in array[NR]) print array[NR][i]}' file

{
    array[NR][1]               # initiate array
    split($0,array[NR],";")    # split to array
        for(i in array[NR])    # iterate thru all array elements
            print array[NR][i] # output array value
}

你可以像这样使用它:

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)
        }
    }
}

指定了翻转标志:

let rotatedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees))

答案 3 :(得分:9)

'调用中的额外参数'通常在其中一个输入类型不正确时发生。

您可以使用

修复代码
var image = UIImage(CGImage: otherImage.CGImage, scale: CGFloat(1.0), orientation: .DownMirrored)

答案 4 :(得分:9)

斯威夫特3:

向右旋转:

let image = UIImage(cgImage: otherImage.cgImage!, scale: CGFloat(1.0), orientation: .right)

在游乐场测试:

// 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()

答案 5 :(得分:5)

试试这段代码,它对我有用:

@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()
        }
    }
}

答案 6 :(得分:4)

@confile答案更新为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!
    }
}

答案 7 :(得分:3)

请勿使用UIGraphicsBeginImageContext,因为它会破坏向量的质量,请使用UIGraphicsBeginImageContextWithOptions。这是我在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
  }

答案 8 :(得分:1)

对于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
}
}

答案 9 :(得分:0)

这适用于iOS 8+并保持图像质量。这是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)
    }
  }

答案 10 :(得分:0)

此代码对我来说可以翻转图像:

let originalImage = UIImage(named: "image")
let flippedImage = originalImage?.withHorizontallyFlippedOrientation()

Documentation

答案 11 :(得分:-1)

检查:

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)
}