Rotating UIImage in Swift

2020-01-26 05:48发布

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.

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.

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.

11条回答
小情绪 Triste *
2楼-- · 2020-01-26 05:53

'Extra argument in call' generally happens when one of the input types is incorrect.

You can fix your code with

var image = UIImage(CGImage: otherImage.CGImage, scale: CGFloat(1.0), orientation: .DownMirrored)
查看更多
SAY GOODBYE
3楼-- · 2020-01-26 05:55

Check this:

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)
}
查看更多
▲ chillily
4楼-- · 2020-01-26 05:58

Do not use UIGraphicsBeginImageContext because it will destroy the quality of your vector, use UIGraphicsBeginImageContextWithOptions. This is my implementation in 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
  }
查看更多
三岁会撩人
5楼-- · 2020-01-26 05:59

For 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
}
}
查看更多
The star\"
6楼-- · 2020-01-26 06:00

Swift 3:

Rotating to right:

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

Tested in Playground:

// 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()
查看更多
叼着烟拽天下
7楼-- · 2020-01-26 06:00

This works for iOS 8+ and maintains the image quality. It's two extensions for 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)
    }
  }
查看更多
登录 后发表回答