从 UIView 蒙版中剪切 UIImage

Cut UIImage from UIView Mask

我有一个 UIView 在特定点具有特定半径的透明 maskLayer。除此之外,我还有一个 UIImageViewUIPanGesture & UIPinchGesture.

现在我可以拖动或缩放 UIImageView 使其适合 UIView 遮罩部分。完成后,我需要从 UIImageView 获得关于透明部分的 UIImage

不知道怎么实现。

下面是在具有指定掩码 CGRect 和半径的 UIView 上创建叠加层的代码。

func createOverlay(frame: CGRect,
                       xOffset: CGFloat,
                       yOffset: CGFloat,
                       radius: CGFloat) -> UIView {
        // Step 1
    let overlayView = UIView(frame: frame)
    overlayView.backgroundColor = UIColor.groupTableViewBackground.withAlphaComponent(0.8)
    // Step 2
    let path = CGMutablePath()
    beizerPath = UIBezierPath(arcCenter: CGPoint(x: xOffset, y: yOffset + radius), radius: radius, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: false)
    path.addArc(center: CGPoint(x: xOffset, y: yOffset),
                     radius: radius,
                    startAngle: 0.0,
                    endAngle: 2.0 * .pi,
                    clockwise: false)
    path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))
    // Step 3
    let maskLayer = CAShapeLayer()
    maskLayer.backgroundColor = UIColor.black.cgColor
    maskLayer.path = path
    maskLayer.fillRule = .evenOdd
    // Step 4
    overlayView.layer.mask = maskLayer
    overlayView.clipsToBounds = true

    return overlayView
}

这里我收藏了UIBezierPath以备不时之需!

然后,我尝试在 UIImage 上剪裁 UIBezierPath,但这是 rect 的问题。因为,UIImageView 可以拖动和缩放所以它是 CGRect 变化。下面是我用来创建剪辑的代码。

extension UIImage {

    func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Mask image using path
        let maskedImage = imageByApplyingMaskingBezierPath(path)

        // Crop image to frame of path
        let croppedImage = UIImage(cgImage: maskedImage.cgImage!.cropping(to: path.bounds)!)
        return croppedImage
    }

    func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Define graphic context (canvas) to paint on
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext()!
        context.saveGState()

        // Set the clipping mask
        path.addClip()
        draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!

        // Restore previous drawing context
        context.restoreGState()
        UIGraphicsEndImageContext()

        return maskedImage
    }

}

其他解决方案,我想拍摄完整视图的快照,然后从中删除 CGRect。但我认为这不是正确的做法!

如果可能,最简单的方法是创建圆形视图并创建该视图的快照。检查以下解决方案:

func cutImageCircle(_ image: UIImage?, inFrame imageFrame: CGRect, contentMode: UIView.ContentMode = .scaleAspectFill, circle: (center: CGPoint, radius: CGFloat)) -> UIImage? {
    guard let image = image else { return nil }

    func generateSnapshotImage(ofView view: UIView, scale: CGFloat = 0.0) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, scale)

        defer { UIGraphicsEndImageContext() }

        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
        return UIGraphicsGetImageFromCurrentImageContext()
    }

    let imageViewPanel = UIView(frame: CGRect(x: 0.0, y: 0.0, width: circle.radius*2.0, height: circle.radius*2.0))
    imageViewPanel.clipsToBounds = true
    imageViewPanel.layer.cornerRadius = circle.radius

    let imageView = UIImageView(frame: {
        var frame = imageFrame
        frame.origin.x -= circle.center.x-circle.radius
        frame.origin.y -= circle.center.y-circle.radius
        return frame
    }())
    imageView.contentMode = contentMode
    imageView.image = image

    imageViewPanel.addSubview(imageView)

    let cutImage = generateSnapshotImage(ofView: imageViewPanel, scale: 1.0)
    return cutImage
}

您只需要计算圆心的位置和大小即可。我在其中添加了调整框架的选项,因此您可以控制输出图像的大小。这样您就可以提高拍摄图像的质量。