使用自定义视图裁剪图像

问题描述

我正在尝试使用自定义视图来裁剪图像。我有两个视图控制器,每个分别是ViewController和ResultViewController。 viewController有一个按钮和一个mageView,另一个一个imageView。当我在ViewController上进行裁剪时,结果超出了ResultViewController上的透明自定义视图。我不知道我在哪里做错或想念。我已将图像视图的内容模式更改为宽高比填充,宽高比适合,缩放以适合和居中,但结果不是我想要的。任何帮助将不胜感激。

我阅读了官方文档here

import UIKit

class ViewController: UIViewController {
    let resultVC = UIStoryboard.init(name: "Main",bundle: nil).instantiateViewController(withIdentifier: "resultVC") as! ResultViewController
    
    var rectangleView: UIView! = UIView(frame: CGRect(x: 100,y: 200,width: 100,height: 100))
    // this is the transparent custom view as a mask.
    
    @IBAction func okayButton(_ sender: Any) {
        resultVC.resultimage = cropImage(imageView.image!,toRect: rectangleView.frame,viewWidth: self.view.frame.size.width,viewHeight: self.view.frame.size.height)
        print(rectangleView.frame)
        present(resultVC,animated: true)
    }
    
    @IBOutlet weak var imageView: UIImageView!
    
    func cropImage(_ inputimage: UIImage,toRect cropRect: CGRect,viewWidth: CGFloat,viewHeight: CGFloat) -> UIImage?
    {
        let imageViewScale = max(inputimage.size.width / viewWidth,inputimage.size.height / viewHeight)
        
        // Scale cropRect to handle images larger than shown-on-screen size
        let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,y:cropRect.origin.y * imageViewScale,width:cropRect.size.width * imageViewScale,height:cropRect.size.height * imageViewScale)
        
        // Perform cropping in Core Graphics
        guard let cutimageRef: CGImage = inputimage.cgImage?.cropping(to:cropZone)
        else {
            return nil
        }
        
        // Return image to UIImage
        let croppedImage: UIImage = UIImage(cgImage: cutimageRef)
        return croppedImage
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let imageFile = UIImage(named: "c.png")
        // Do any additional setup after loading the view.
        
        imageView.image = imageFile
        
        view.addSubview(rectangleView)
        rectangleView.backgroundColor = UIColor.init(white: 1,alpha: 0.5)
        
        
    }
    
    
}

//ResultViewController
import UIKit

class ResultViewController: UIViewController {
    var resultimage:UIImage!
    @IBOutlet weak var resultimageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        resultimageView.image = resultimage
        // Do any additional setup after loading the view.
    }
}

裁剪前1

裁剪后2

解决方法

这里重要的一点是,在裁剪之前必须将所有帧的矩形转换为相同的坐标系。

为简便起见,我将选择图像作为基本坐标系,其原点为图像的最左点。请注意,我在这里提到的图像与用于显示图像的imageView不同。

对于裁剪功能,我将使用my library

public func crop(image: UIImage,toRect rect: CGRect) -> UIImage {
    let orientation = image.imageOrientation
    let scale = image.scale
    var targetRect = CGRect()
    
    switch orientation {
    case .down:
        targetRect.origin.x = (image.size.width - rect.maxX) * scale
        targetRect.origin.y = (image.size.height - rect.maxY) * scale
        targetRect.size.width = rect.width * scale
        targetRect.size.height = rect.height * scale
    case .right:
        targetRect.origin.x = rect.minY * scale
        targetRect.origin.y = (image.size.width - rect.maxX) * scale
        targetRect.size.width = rect.height * scale
        targetRect.size.height = rect.width * scale
    case .left:
        targetRect.origin.x = image.size.height - rect.maxY * scale
        targetRect.origin.y = rect.minX * scale
        targetRect.size.width = rect.height * scale
        targetRect.size.height = rect.width * scale
    default:
        targetRect = CGRect(x: rect.origin.x * scale,y: rect.origin.y * scale,width: rect.width * scale,height: rect.height * scale)
    }
    
    if let croppedCGImage = image.cgImage?.cropping(to: targetRect) {
        return UIImage(cgImage: croppedCGImage,scale: scale,orientation: orientation)
    }
    
    return image
}

要在示例中正确使用上述功能,必须将实现更改为此。请阅读评论以了解想法。

@IBAction func okayButton(_ sender: Any) {
    // Get the rectangeView's frame in the imageView's coordinate system
    let rectangleViewRectInImageView = rectangleView.convert(rectangleView.bounds,to: imageView)
    
    let imageWHRatio = imageView.image!.size.width / imageView.image!.size.height
    let imageViewWHRatio = imageView.bounds.width / imageView.bounds.height
    
    // We are going to calculate the frame of the displayed image content inside the imageView
    var imageRectInImageView: CGRect = .zero
    
    if imageWHRatio > imageViewWHRatio {
        // White spaces in the top & bottom
        imageRectInImageView = CGRect(
            x: 0,y: (imageView.bounds.height - imageView.bounds.width / imageWHRatio) / 2,width: imageView.bounds.width,height: imageView.bounds.width / imageWHRatio
        )
    } else {
        // White spaces in the left & right
        imageRectInImageView = CGRect(
            x: (imageView.bounds.width - imageView.bounds.height * imageWHRatio) / 2,y: 0,width: imageView.bounds.height * imageWHRatio,height: imageView.bounds.height
        )
    }
    
    // How big the image are being scaled to fit the imageView's bound.
    let imageScale = imageView.image!.size.width / imageRectInImageView.width
    
    // The frame of the rectangeView in the displayed image content view's coordinate system
    let toRect = CGRect(
        x: (rectangleViewRectInImageView.minX - imageRectInImageView.minX) * imageScale,y: (rectangleViewRectInImageView.minY - imageRectInImageView.minY) * imageScale,width: rectangleViewRectInImageView.width * imageScale,height: rectangleViewRectInImageView.height * imageScale)
    
    // Now you have the frame of the rectangleView in proper coordinate system,so you can my method above safely
    resultVC.resultImage = crop(image: imageView.image!,toRect: toRect)
    print(rectangleView.frame)
    present(resultVC,animated: true)
}

请确保您必须选择scaleAspectFill作为imageView的contentMode,才能使其正常工作。