如何在“纵横填充”模式下将UIImageView裁剪为新的UIImage?

我试图裁剪一个图像视图的子图像使用覆盖UIView ,可以在UIImageView任何位置。 我从类似的post借用一个解决scheme,当UIImageView内容模式是“Aspect Fit”时,如何解决这个问题。 该提议的解决scheme是:

  func computeCropRect(for sourceFrame : CGRect) -> CGRect { let widthScale = bounds.size.width / image!.size.width let heightScale = bounds.size.height / image!.size.height var x : CGFloat = 0 var y : CGFloat = 0 var width : CGFloat = 0 var height : CGFloat = 0 var offSet : CGFloat = 0 if widthScale < heightScale { offSet = (bounds.size.height - (image!.size.height * widthScale))/2 x = sourceFrame.origin.x / widthScale y = (sourceFrame.origin.y - offSet) / widthScale width = sourceFrame.size.width / widthScale height = sourceFrame.size.height / widthScale } else { offSet = (bounds.size.width - (image!.size.width * heightScale))/2 x = (sourceFrame.origin.x - offSet) / heightScale y = sourceFrame.origin.y / heightScale width = sourceFrame.size.width / heightScale height = sourceFrame.size.height / heightScale } return CGRect(x: x, y: y, width: width, height: height) } 

问题是使用这个解决scheme时,图像视图是方面填充导致裁剪段不准确地排列叠加UIView位置。 我不太清楚如何调整这个代码,以适应Aspect Fill或重新定位我的覆盖UIView ,使它与我试图裁剪的段1:1排队。

更新解决使用马特的答案如下

 class ViewController: UIViewController { @IBOutlet weak var catImageView: UIImageView! private var cropView : CropView! override func viewDidLoad() { super.viewDidLoad() cropView = CropView(frame: CGRect(x: 0, y: 0, width: 45, height: 45)) catImageView.image = UIImage(named: "cat") catImageView.clipsToBounds = true catImageView.layer.borderColor = UIColor.purple.cgColor catImageView.layer.borderWidth = 2.0 catImageView.backgroundColor = UIColor.yellow catImageView.addSubview(cropView) let imageSize = catImageView.image!.size let imageViewSize = catImageView.bounds.size var scale : CGFloat = imageViewSize.width / imageSize.width if imageSize.height * scale < imageViewSize.height { scale = imageViewSize.height / imageSize.height } let croppedImageSize = CGSize(width: imageViewSize.width/scale, height: imageViewSize.height/scale) let croppedImrect = CGRect(origin: CGPoint(x: (imageSize.width-croppedImageSize.width)/2.0, y: (imageSize.height-croppedImageSize.height)/2.0), size: croppedImageSize) let renderer = UIGraphicsImageRenderer(size:croppedImageSize) let _ = renderer.image { _ in catImageView.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y)) } } @IBAction func performCrop(_ sender: Any) { let cropFrame = catImageView.computeCropRect(for: cropView.frame) if let imageRef = catImageView.image?.cgImage?.cropping(to: cropFrame) { catImageView.image = UIImage(cgImage: imageRef) } } @IBAction func resetCrop(_ sender: Any) { catImageView.image = UIImage(named: "cat") } } 

最终结果 在这里输入图像说明

我们把问题分成两部分:

  1. 鉴于UIImageView的大小和它的UIImage的大小, 如果UIImageView的内容模式是Aspect Fill, UIImage的哪一部分适合UIImageView? 实际上,我们需要裁剪原始图像以匹配UIImageView实际显示的内容。

  2. 给定UIImageView中的任意矩形, 裁剪图像的哪一部分(在第1部分中导出)是否对应于?

第一部分是有趣的部分,所以我们来试试吧。 (第二部分将变得微不足道。)

这是我将要使用的原始图像:

https://static1.squarespace.com/static/54e8ba93e4b07c3f655b452e/t/56c2a04520c64707756f4267/1455596221531/

该图像是1000×611。 下面是它缩小的样子(但请记住,我将始终使用原始图像):

在这里输入图像说明

然而,我的图像视图将是139×182,并设置为Aspect Fill。 当它显示图像时,它看起来像这样:

在这里输入图像说明

我们要解决的问题是:如果我的图像视图设置为Aspect Fill,我的图像视图中显示原始图像的哪一部分?

开始了。 假设iv是图像视图:

 let imsize = iv.image!.size let ivsize = iv.bounds.size var scale : CGFloat = ivsize.width / imsize.width if imsize.height * scale < ivsize.height { scale = ivsize.height / imsize.height } let croppedImsize = CGSize(width:ivsize.width/scale, height:ivsize.height/scale) let croppedImrect = CGRect(origin: CGPoint(x: (imsize.width-croppedImsize.width)/2.0, y: (imsize.height-croppedImsize.height)/2.0), size: croppedImsize) 

所以现在我们已经解决了这个问题: croppedImrect是在图像视图中显示的原始图像的区域。 让我们继续使用我们的知识,实际上将图像剪裁成与图像视图中显示的内容匹配的新图像:

 let r = UIGraphicsImageRenderer(size:croppedImsize) let croppedIm = r.image { _ in iv.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y)) } 

结果是这个图像(忽略灰色边框):

在这里输入图像说明

但是,瞧,那是正确的答案! 我从原始图像中提取了在图像视图内部刻画的区域。

所以现在你有所有你需要的信息。 croppedIm是实际显示在图像视图的剪切区域中的UIImage。 scale是图像视图和该图像之间的尺度。 因此,您可以轻松解决您最初提出的问题! 给定图像视图上的任何矩形,在图像视图的边界坐标中,只需应用比例(即按scale划分其全部四个属性) – 现在,您可以使用与croppedIm的一部分相同的矩形。

(注意到我们并不需要裁剪原始图像来剪裁图像,实际上,知道如何去完成这个作物就足够了,重要的信息就是scalecroppedImRectorigin ;给定信息,您可以将矩形强加在图像视图上,对其进行缩放并将其偏移,以获得原始图像的所需矩形。)


编辑我添加了一个小屏幕截图,以显示我的方法作为概念的certificate:

在这里输入图像说明