【问题标题】:Cropping image with Swift and put it on center position使用 Swift 裁剪图像并将其放在中心位置
【发布时间】:2015-11-09 13:27:30
【问题描述】:

在 Swift 编程中,如何裁剪图像并将其放在中心?

这是我到目前为止所得到的......我已经成功裁剪了图像,但我想把它放在中心

ImgView.image = OrigImage
var masklayer = CAShapeLayer()
masklayer.frame = ImgView.frame
masklayer.path = path.CGPath
masklayer.fillColor = UIColor.whiteColor().CGColor
masklayer.backgroundColor = UIColor.clearColor().CGColor

ImgView.layer.mask = masklayer

UIGraphicsBeginImageContext(ImgView.bounds.size);
ImgView.layer.renderInContext(UIGraphicsGetCurrentContext())
var image = UIGraphicsGetImageFromCurrentImageContext()
ImgView.image = image
UIGraphicsEndImageContext();

更新:

let rect: CGRect = CGRectMake(path.bounds.minX, path.bounds.minY, path.bounds.width, path.bounds.height)

// Create bitmap image from context using the rect
let imageRef: CGImageRef = CGImageCreateWithImageInRect(image.CGImage, rect)
ImgView.bounds = rect
ImgView.image = UIImage(CGImage: imageRef)

我能够通过获取 path.bound 和 size 并更改 ImageView 的边界来使其居中。 :)

【问题讨论】:

  • 请展示您已经尝试过的内容。

标签: ios swift


【解决方案1】:

要获得作物的居中位置,您可以将高度和宽度的差异减半。然后您可以在检查图像的方向(哪个部分较长)后为新的宽度和高度分配边界

func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {

    let contextImage: UIImage = UIImage(CGImage: image.CGImage)!

    let contextSize: CGSize = contextImage.size

    var posX: CGFloat = 0.0
    var posY: CGFloat = 0.0
    var cgwidth: CGFloat = CGFloat(width)
    var cgheight: CGFloat = CGFloat(height)

    // See what size is longer and create the center off of that
    if contextSize.width > contextSize.height {
        posX = ((contextSize.width - contextSize.height) / 2)
        posY = 0
        cgwidth = contextSize.height
        cgheight = contextSize.height
    } else {
        posX = 0
        posY = ((contextSize.height - contextSize.width) / 2)
        cgwidth = contextSize.width
        cgheight = contextSize.width
    }

    let rect: CGRect = CGRectMake(posX, posY, cgwidth, cgheight)

    // Create bitmap image from context using the rect
    let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)

    // Create a new image based on the imageRef and rotate back to the original orientation
    let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)!

    return image
}

我在website 找到了大部分信息,以防您想进一步阅读。

Swift 4

更新
func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {

        let cgimage = image.cgImage!
        let contextImage: UIImage = UIImage(cgImage: cgimage)
        let contextSize: CGSize = contextImage.size
        var posX: CGFloat = 0.0
        var posY: CGFloat = 0.0
        var cgwidth: CGFloat = CGFloat(width)
        var cgheight: CGFloat = CGFloat(height)

        // See what size is longer and create the center off of that
        if contextSize.width > contextSize.height {
            posX = ((contextSize.width - contextSize.height) / 2)
            posY = 0
            cgwidth = contextSize.height
            cgheight = contextSize.height
        } else {
            posX = 0
            posY = ((contextSize.height - contextSize.width) / 2)
            cgwidth = contextSize.width
            cgheight = contextSize.width
        }

        let rect: CGRect = CGRect(x: posX, y: posY, width: cgwidth, height: cgheight)

        // Create bitmap image from context using the rect
        let imageRef: CGImage = cgimage.cropping(to: rect)!

        // Create a new image based on the imageRef and rotate back to the original orientation
        let image: UIImage = UIImage(cgImage: imageRef, scale: image.scale, orientation: image.imageOrientation)

        return image
    }

【讨论】:

  • 我得到了上面的一些代码,并使用 path.bound 将它放在中心并且它工作......(grrrr......我似乎无法发布我的代码,无论如何我'会更新我上面的问题)
  • 您的解决方案对我有用。只是想知道是否有任何方法可以制作裁剪动画。
  • 为什么有宽度/高度参数?
  • 让 cgimage = image.cgImage! let contextImage: UIImage = UIImage(cgImage: cgimage) 这两行不是必须的。您正在将 UIImage 转换为 CGImage 并返回。
  • 让 cgimage = image.cgImage! let contextImage: UIImage = UIImage(cgImage: cgimage) 需要这两行代码来制作比例为 1.0 的 contextImage
【解决方案2】:

改变这个:

masklayer.frame = ImgView.frame

到这里:

masklayer.frame = ImgView.bounds

【讨论】:

    【解决方案3】:

    接受的答案只对我有用。我需要更灵活的裁剪机制,所以我写了一个扩展如下:

    导入 UIKit

    extension UIImage {
    
    func crop(to:CGSize) -> UIImage {
    
        guard let cgimage = self.cgImage else { return self }
    
        let contextImage: UIImage = UIImage(cgImage: cgimage)
    
        guard let newCgImage = contextImage.cgImage else { return self }
    
        let contextSize: CGSize = contextImage.size
    
        //Set to square
        var posX: CGFloat = 0.0
        var posY: CGFloat = 0.0
        let cropAspect: CGFloat = to.width / to.height
    
        var cropWidth: CGFloat = to.width
        var cropHeight: CGFloat = to.height
    
        if to.width > to.height { //Landscape
            cropWidth = contextSize.width
            cropHeight = contextSize.width / cropAspect
            posY = (contextSize.height - cropHeight) / 2
        } else if to.width < to.height { //Portrait
            cropHeight = contextSize.height
            cropWidth = contextSize.height * cropAspect
            posX = (contextSize.width - cropWidth) / 2
        } else { //Square
            if contextSize.width >= contextSize.height { //Square on landscape (or square)
                cropHeight = contextSize.height
                cropWidth = contextSize.height * cropAspect
                posX = (contextSize.width - cropWidth) / 2
            }else{ //Square on portrait
                cropWidth = contextSize.width
                cropHeight = contextSize.width / cropAspect
                posY = (contextSize.height - cropHeight) / 2
            }
        }
    
        let rect: CGRect = CGRect(x: posX, y: posY, width: cropWidth, height: cropHeight)
    
        // Create bitmap image from context using the rect
        guard let imageRef: CGImage = newCgImage.cropping(to: rect) else { return self}
    
        // Create a new image based on the imageRef and rotate back to the original orientation
        let cropped: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
    
        UIGraphicsBeginImageContextWithOptions(to, false, self.scale)
        cropped.draw(in: CGRect(x: 0, y: 0, width: to.width, height: to.height))
        let resized = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        return resized ?? self
      }
    }
    

    你可以这样使用它:

    let size = CGSize(width: 300, height: 200)
    let image = UIImage(named: "my_great_photo")?.crop(size)
    

    如果有人知道如何更好地处理横向、纵向和正方形,请告诉我。

    【讨论】:

    • 这对我不起作用。我只得到一个黑匣子。变量cropped 不在任何地方使用。猜猜这就是问题所在。
    • 是的。对不起。有一个错字。现在底部的行说:cropped.drawInRect(CGRectMake(0, 0, to.width, to.height)) 这意味着裁剪的图像将被调整大小。我有不同的调整大小和裁剪方法,因此之前是 self.drawInRect。
    • 哇,谢谢。你为我节省了数百万小时。这很有帮助。一个问题是在裁剪图像时,图像的分辨率会变小。是否可以在裁剪图像的同时保持图像质量?
    • 图像即将出现,但裁剪区域变黑。我怎样才能把它变成白色。谢谢
    • @TanelTeemusk 当我从相机捕捉图像时自动旋转。
    【解决方案4】:

    您也可以非常简单地从 Storyboard 中将相关的 ImageView 置于“Aspect Fill”模式,并将其添加到代码中:

    imageView.layer.masksToBounds = true
    imageView.clipsToBounds = true

    【讨论】:

      【解决方案5】:

      接受的答案对我不起作用,所以我尝试自己编写。这是我的工作效果:

      import UIKit
      
      extension UIImage {
      
          func cropedToRatio(ratio: CGFloat) -> UIImage? {
              let newImageWidth = size.height * ratio
      
              let cropRect = CGRect(x: ((size.width - newImageWidth) / 2.0) * scale,
                                    y: 0.0,
                                    width: newImageWidth * scale,
                                    height: size.height * scale)
      
              guard let cgImage = cgImage else {
                  return nil
              }
              guard let newCgImage = cgImage.cropping(to: cropRect) else {
                  return nil
              }
      
              return UIImage(cgImage: newCgImage, scale: scale, orientation: imageOrientation)
          }
      }
      

      此功能将图像裁剪为给定的比例。它保持图像比例。裁剪后的图像始终位于原始图像的中心。

      【讨论】:

      • 这个可以用来得到16:9吗?
      【解决方案6】:

      给科尔的道具

      斯威夫特 3

      func crop(image: UIImage, withWidth width: Double, andHeight height: Double) -> UIImage? {
          
          if let cgImage = image.cgImage {
              
              let contextImage: UIImage = UIImage(cgImage: cgImage)
              
              let contextSize: CGSize = contextImage.size
              
              var posX: CGFloat = 0.0
              var posY: CGFloat = 0.0
              var cgwidth: CGFloat = CGFloat(width)
              var cgheight: CGFloat = CGFloat(height)
              
              // See what size is longer and create the center off of that
              if contextSize.width > contextSize.height {
                  posX = ((contextSize.width - contextSize.height) / 2)
                  posY = 0
                  cgwidth = contextSize.height
                  cgheight = contextSize.height
              } else {
                  posX = 0
                  posY = ((contextSize.height - contextSize.width) / 2)
                  cgwidth = contextSize.width
                  cgheight = contextSize.width
              }
              
              let rect: CGRect = CGRect(x: posX, y: posY, width: cgwidth, height: cgheight)
              
              // Create bitmap image from context using the rect
              var croppedContextImage: CGImage? = nil
              if let contextImage = contextImage.cgImage {
                  if let croppedImage = contextImage.cropping(to: rect) {
                      croppedContextImage = croppedImage
                  }
              }
              
              // Create a new image based on the imageRef and rotate back to the original orientation
              if let croppedImage:CGImage = croppedContextImage {
                  let image: UIImage = UIImage(cgImage: croppedImage, scale: image.scale, orientation: image.imageOrientation)
                  return image
              }
              
          }
          
          return nil
      }
      

      【讨论】:

      • 该方法的参数对结果没有任何影响
      【解决方案7】:

      工作 Swift 3 示例

      extension UIImage {
      
          func crop(to:CGSize) -> UIImage {
              guard let cgimage = self.cgImage else { return self }
      
              let contextImage: UIImage = UIImage(cgImage: cgimage)
      
              let contextSize: CGSize = contextImage.size
      
              //Set to square
              var posX: CGFloat = 0.0
              var posY: CGFloat = 0.0
              let cropAspect: CGFloat = to.width / to.height
      
              var cropWidth: CGFloat = to.width
              var cropHeight: CGFloat = to.height
      
              if to.width > to.height { //Landscape
                  cropWidth = contextSize.width
                  cropHeight = contextSize.width / cropAspect
                  posY = (contextSize.height - cropHeight) / 2
              } else if to.width < to.height { //Portrait
                  cropHeight = contextSize.height
                  cropWidth = contextSize.height * cropAspect
                  posX = (contextSize.width - cropWidth) / 2
              } else { //Square
                  if contextSize.width >= contextSize.height { //Square on landscape (or square)
                      cropHeight = contextSize.height
                      cropWidth = contextSize.height * cropAspect
                      posX = (contextSize.width - cropWidth) / 2
                  }else{ //Square on portrait
                      cropWidth = contextSize.width
                      cropHeight = contextSize.width / cropAspect
                      posY = (contextSize.height - cropHeight) / 2
                  }
              }
      
              let rect: CGRect = CGRect(x: posX, y: posY, width: cropWidth, height: cropHeight)
              // Create bitmap image from context using the rect
              let imageRef: CGImage = contextImage.cgImage!.cropping(to: rect)!
      
              // Create a new image based on the imageRef and rotate back to the original orientation
              let cropped: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
      
              UIGraphicsBeginImageContextWithOptions(to, true, self.scale)
              cropped.draw(in: CGRect(x: 0, y: 0, width: to.width, height: to.height))
              let resized = UIGraphicsGetImageFromCurrentImageContext()
              UIGraphicsEndImageContext()
      
              return resized!
          }
      }
      

      【讨论】:

        【解决方案8】:

        你可以试试这个答案。 它是用swift 3编写的。

        extension UIImage {
          func crop(to:CGSize) -> UIImage {
            guard let cgimage = self.cgImage else { return self }
        
            let contextImage: UIImage = UIImage(cgImage: cgimage)
        
            let contextSize: CGSize = contextImage.size
        
            //Set to square
            var posX: CGFloat = 0.0
            var posY: CGFloat = 0.0
            let cropAspect: CGFloat = to.width / to.height
        
            var cropWidth: CGFloat = to.width
            var cropHeight: CGFloat = to.height
        
            if to.width > to.height { //Landscape
                cropWidth = contextSize.width
                cropHeight = contextSize.width / cropAspect
                posY = (contextSize.height - cropHeight) / 2
            } else if to.width < to.height { //Portrait
                cropHeight = contextSize.height
                cropWidth = contextSize.height * cropAspect
                posX = (contextSize.width - cropWidth) / 2
            } else { //Square
                if contextSize.width >= contextSize.height { //Square on landscape (or square)
                    cropHeight = contextSize.height
                    cropWidth = contextSize.height * cropAspect
                    posX = (contextSize.width - cropWidth) / 2
                }else{ //Square on portrait
                    cropWidth = contextSize.width
                    cropHeight = contextSize.width / cropAspect
                    posY = (contextSize.height - cropHeight) / 2
                }
            }
        
            let rect: CGRect = CGRect(x : posX, y : posY, width : cropWidth, height : cropHeight)
        
            // Create bitmap image from context using the rect
            let imageRef: CGImage = contextImage.cgImage!.cropping(to: rect)!
        
            // Create a new image based on the imageRef and rotate back to the original orientation
            let cropped: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
        
            cropped.draw(in: CGRect(x : 0, y : 0, width : to.width, height : to.height))
        
            return cropped
          }
        }
        

        【讨论】:

        • 完美运行
        • 适用于大图像,但也为小图像留出空间。
        【解决方案9】:

        您可以使用以下方法进行裁剪:

        let croppedImage = yourImage.cgImage.cropping(to:rect)
        

        【讨论】:

          【解决方案10】:

          这就是答案,感谢 @awolf (Cropping an UIImage)。完美处理比例和方向。只需在要裁剪的图像上调用此方法,并传入裁剪CGRect,而无需担心缩放或方向。随意检查 cgImage 是否为 nil,而不是像我在这里那样强制解包。

          extension UIImage {
              func croppedInRect(rect: CGRect) -> UIImage {
                  func rad(_ degree: Double) -> CGFloat {
                      return CGFloat(degree / 180.0 * .pi)
                  }
          
                  var rectTransform: CGAffineTransform
                  switch imageOrientation {
                  case .left:
                      rectTransform = CGAffineTransform(rotationAngle: rad(90)).translatedBy(x: 0, y: -self.size.height)
                  case .right:
                      rectTransform = CGAffineTransform(rotationAngle: rad(-90)).translatedBy(x: -self.size.width, y: 0)
                  case .down:
                      rectTransform = CGAffineTransform(rotationAngle: rad(-180)).translatedBy(x: -self.size.width, y: -self.size.height)
                  default:
                      rectTransform = .identity
                  }
                  rectTransform = rectTransform.scaledBy(x: self.scale, y: self.scale)
          
                  let imageRef = self.cgImage!.cropping(to: rect.applying(rectTransform))
                  let result = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
                  return result
              }
          }
          

          如果您希望裁剪矩形居中,只需进行简单的数学运算即可。沿着

          let x = (image.width - croppingFrame.width) / 2
          

          另一个注意事项:如果您使用嵌入在scrollView 中的imageView,还有一个额外的步骤,您必须考虑缩放系数。假设您的imageView 跨越了scrollView 的整个内容视图,并且您使用scrollView 的边界作为裁剪框,则裁剪后的图像可以获得为

          let ratio = imageView.image!.size.height / scrollView.contentSize.height
          let origin = CGPoint(x: scrollView.contentOffset.x * ratio, y: scrollView.contentOffset.y * ratio)
          let size = CGSize(width: scrollView.bounds.size.width * ratio, let height: scrollView.bounds.size.height * ratio)
          let cropFrame = CGRect(origin: origin, size: size)
          let croppedImage = imageView.image!.croppedInRect(rect: cropFrame)
          

          【讨论】:

          • "let x = (image.width - croppingFrame.width) / 2" -> 我需要把这条线放在哪里?谢谢
          【解决方案11】:

          或者做UImage扩展

          extension UIImage {
              func cropped(boundingBox: CGRect) -> UIImage? {
                  guard let cgImage = self.cgImage?.cropping(to: boundingBox) else {
                      return nil
                  }
          
                  return UIImage(cgImage: cgImage)
              }
          }
          

          【讨论】:

            【解决方案12】:

            您还可以使用 Alamofire 和 AlamofireImage 来裁剪图像。

            https://github.com/Alamofire/AlamofireImage

            使用 CocoaPods 安装 pod 'AlamofireImage'

            用法:

            let image = UIImage(named: "unicorn")!
            let size = CGSize(width: 100.0, height: 100.0)
            // Scale image to size disregarding aspect ratio
            let scaledImage = image.af_imageScaled(to: size)
            let aspectScaledToFitImage = image.af_imageAspectScaled(toFit: size)
            
            // Scale image to fill specified size while maintaining aspect ratio
            let aspectScaledToFillImage = image.af_imageAspectScaled(toFill: size)
            

            【讨论】:

              【解决方案13】:

              在 swift 4.1 中,我会这样做:

              imageView.clipsToBounds = true
              imageView.contentMode = .scaleAspectFill
              imageView.layer.cornerRadius = 20
              

              感谢Stretching, Redrawing and Positioning with contentMode

              【讨论】:

                【解决方案14】:

                我想出了一个代码,它可以提供所需的裁剪纵横比,而不管原始视频帧大小如何(改编自 @Cole 的回答):

                func cropImage(uncroppedImage: UIImage, cropWidth: CGFloat, cropHeight: CGFloat) -> UIImage {
                
                        let contextImage: UIImage = UIImage(cgImage: uncroppedImage.cgImage!)
                
                        let contextSize: CGSize = contextImage.size
                        var cropX: CGFloat = 0.0
                        var cropY: CGFloat = 0.0
                        var cropRatio: CGFloat = CGFloat(cropWidth/cropHeight)
                        var originalRatio: CGFloat = contextSize.width/contextSize.height
                        var scaledCropHeight: CGFloat = 0.0
                        var scaledCropWidth: CGFloat = 0.0
                
                        // See what size is longer and set crop rect parameters
                        if originalRatio > cropRatio {
                
                            scaledCropHeight = contextSize.height
                            scaledCropWidth = (contextSize.height/cropHeight) * cropWidth
                            cropX = (contextSize.width - scaledCropWidth) / 2
                            cropY = 0
                
                        } else {
                            scaledCropWidth = contextSize.width
                            scaledCropHeight = (contextSize.width/cropWidth) * cropHeight
                            cropY = (contextSize.height / scaledCropHeight) / 2
                            cropX = 0
                        }
                
                        let rect: CGRect = CGRect(x: cropX, y: cropY, width: scaledCropWidth, height: scaledCropHeight)
                
                        // Create bitmap image from context using the rect
                        let imageRef: CGImage = contextImage.cgImage!.cropping(to: rect)!
                
                        // Create a new image based on the imageRef and rotate back to the original orientation
                
                        let croppedImage: UIImage = UIImage(cgImage: imageRef, scale: uncroppedImage.scale, orientation: uncroppedImage.imageOrientation)
                
                        return croppedImage
                    }
                

                希望对你有帮助!

                【讨论】:

                  【解决方案15】:

                  斯威夫特 5

                  extension UIImage {
                      /// A function who takes in a uiimage and crops it to its largest square value
                      /// - Returns: The cropped image. Nil if the data could not be extracted from the UIImage
                      internal func croppedToSquare() -> UIImage? {
                          guard let sourceImageData = self.cgImage else {
                              return nil
                          }
                      
                          let shortestSide = min(self.size.width, self.size.height)
                      
                          // Determines the x,y coordinate of the top-left corner of the cropped photo
                          /// The distance from the leading edge of the original photo to the leading edge of the cropped photo (should be 0 for photos in portrait orientation)
                          let xOffset = (self.size.width - shortestSide) / 2
                          /// The distance from the top edge of the original photo to the top edge of the cropped photo (should be 0 for photos in landscape orientation)
                          let yOffset = (self.size.height - shortestSide) / 2
                      
                          /// A boolean which indicates if the image data is a rotation (reflections don't matter) of the uiimage. If so, they x and y coordinates should be transposed.
                          let axesAreFlipped = self.imageOrientation == .left || self.imageOrientation == .right
                          /// The square to crop the image through, with the x, y coordinate describing the top left corner of the square
                          let cropMask = CGRect(x: axesAreFlipped ? yOffset : xOffset, y: axesAreFlipped ? xOffset : yOffset, width: shortestSide, height: shortestSide).integral
                      
                          guard let newImageData = sourceImageData.cropping(to: cropMask) else {
                              return nil
                          }
                      
                          return UIImage(cgImage: newImageData, scale: self.imageRendererFormat.scale, orientation: self.imageOrientation)
                      }
                  }
                  

                  这个函数的工作原理是从给定的 UIImage 中提取 CGImage 数据,然后找到照片左上角的坐标。从这个坐标出发,绘制一个边长相等的矩形(边长是原始图像的最短边)。

                  请注意,CGImage 数据描述的是包装在 UIImage 中的原始照片的数据,而不是 UIImage 默认显示的数据。因此,需要检查图像的原始方向,以确定是否需要翻转 x 和 y 值。

                  【讨论】:

                    猜你喜欢
                    • 2019-07-09
                    • 2020-01-11
                    • 1970-01-01
                    • 2018-10-23
                    • 1970-01-01
                    • 2011-10-18
                    • 2014-05-26
                    • 2019-05-13
                    • 1970-01-01
                    相关资源
                    最近更新 更多