【问题标题】:Rotating UIImage in Swift在 Swift 中旋转 UIImage
【发布时间】:2014-11-23 18:02:13
【问题描述】:

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

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

当我有

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

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

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

你怎么看?

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

【问题讨论】:

  • Xcode 6.0.1 已经过时(不知道是不是这个问题)。您的代码在我的 Xcode 6.1 中编译时没有错误或警告。仅当您省略 .CGImage(如引用的 github 页面中所示)时,您才会收到编译器错误。
  • 如果您喜欢这个 Q/A stackoverflow.com/questions/40882487/…,您可以轻松旋转图像视图来绘制图像

标签: ios swift uiimage


【解决方案1】:

Swift 5 解决方案

我写了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
    }
}

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

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

【讨论】:

  • 这是一个很好的解决方案。我使用了另一个在旋转后剪切了我的图像,但是这个效果很好。
  • 在处理具有不同方向的 UIImage 时要小心。 self.draw() 将自动以 .up 方向绘制图像。
  • 我正在使用这个尝试在自定义 annotationView 中旋转 uiimage。该图像是摩托车的 .png。它没有旋转图像......它只是返回一个黑色方块?
  • .pi 是 180 度,.pi/2 是 90 度,.pi/4 是 45 度,...,.pi/180 是 1 度。
  • 好答案。你救了我的一天!
【解决方案2】:

这是 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,前者将旋转图像并翻转它。

【讨论】:

  • 配置文件,翻转效果很棒。旋转的问题是确实旋转了图像,但是 - 它改变了纵横比并“破坏它”,你能检查它并修改代码吗?那会很好。谢谢!
  • 还发现它可以修改比率 - 非常接近!
  • UIGraphicsBeginImageContext(rotatedSize) 应该是 UIGraphicsBeginImageContextWithOptions(rotatedSize, false, self.scale) 以保持比例
  • Xcode 建议将此代码转换为 Swift 3,但 CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage) 这一行除外。将其更改为bitmap?.draw( cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2,width: size.width,height: size.height))
  • 这种方式不会释放内存。每次我旋转使用 iPhone 6 拍摄的新照片时,它都会保留大约 30MB 内存。
【解决方案3】:
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))

指定翻转标志:

let flippedImage = UIImage(named: "my_amazing_image")?.rotated(by: Measurement(value: 48.0, unit: .degrees), options: [.flipOnVerticalAxis])

【讨论】:

  • 谢谢,很好的解决方案。您能否提供翻转图像的解决方案?
  • @ixany 我已经更新了在垂直轴上翻转的答案。你是这个意思吗?
  • 这正是我想要的。再次感谢你! :)
  • 我的研究到此结束,谢谢!!
  • 此功能存在严重问题。当 imageOrientation != UIImage.Orientation.up 时,函数给出完全错误的图像。
【解决方案4】:

斯威夫特 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()

【讨论】:

  • 为什么不呢?给我看看你的 Playground 文件!
  • 这就是答案!谢谢!
  • 这并没有回答问题,因为他们要求一种方法来制作另一个图像。此解决方案仅使用方向 EXIF 数据来显示向右转动的图像。因此,例如,您不能调用两次来将图像旋转 180 度。
  • @typewrite:你是对的,但它可以帮助其他人旋转 UIImages
【解决方案5】:

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

你可以修复你的代码

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

【讨论】:

    【解决方案6】:

    @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】:

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

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

      【讨论】:

      • 我将它转换为 Swift4 但它会导致内存问题。
      • 使用 UIGraphicsEndImageContext() 后内存问题解决了。
      • 当拍摄landscapeLeft图像然后显示图像是错误的一面。
      【解决方案8】:

      不要使用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
        }
      

      【讨论】:

      • 我如何使用这个扩展。我尝试这样使用它:让 originalImage = UIImage(named: "image").rotated(degrees: 90) 并没有工作。
      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

        这适用于 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)
            }
          }
        

        【讨论】:

          【解决方案11】:

          如果您需要水平翻转图像或者如果您需要原始图像的镜像,有一个简单的方法:

          此代码适用于我翻转图像:

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

          Documentation

          【讨论】:

            【解决方案12】:

            检查一下:

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

            【讨论】:

              【解决方案13】:

              使用 Swift 5 Xcode 13 测试

              extension UIView{
              
              func rotate(degrees: CGFloat) {
              
                      let degreesToRadians: (CGFloat) -> CGFloat = { (degrees: CGFloat) in
                          return degrees / 180.0 * CGFloat.pi
                      }
                      self.transform =  CGAffineTransform(rotationAngle: degreesToRadians(degrees))
                  }
              

              }

              以度为单位传递角度

              myView.rotate(degrees : 90)
              

              【讨论】:

                猜你喜欢
                • 2015-05-12
                • 2021-08-04
                • 2016-08-24
                • 2018-09-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-01-29
                • 2010-12-17
                相关资源
                最近更新 更多