如何从 caShape 层裁剪图像的形状,

How to crop the shape of an image out of a caShape layer,

我有一个看起来像这样的 cashape 层

我有这张P图

我希望三角形的中心在相同形状的 P 图像(或任何图像)中是透明的,就像这样

我已经尝试过(在许多其他事情中)这个...看起来很接近,但它只显示 P,我需要它来显示除 P

之外的所有内容
 let viewTheTriangleIsIn = UIView()
    let pImage = UIImageView()

    let maskLayer = CAShapeLayer()
    maskLayer.frame.origin = CGPoint(x: 0, y: 0)
    maskLayer.frame.size = viewTheTriangleIsIn.bounds.size
    
    maskLayer.contents = pImage.image!.cgImage
    
    maskLayer.fillRule = .evenOdd
    viewTheTriangleIsIn.layer.mask = maskLayer

假设您的“P 图像(或任何图像)” 具有透明(alpha)区域,例如(P 被透明度包围):

所以它在 UIImageView:

中看起来像这样

您可以创建“反转透明”图像:

    guard let pImage = UIImage(named: "pImage") else {
        print("Could not load mask image!")
        return
    }
    
    // size of view you want to mask
    let sz: CGSize = CGSize(width: 200, height: 200)
    // rect to draw the mask image (where we want the "P")
    let maskRect:CGRect = CGRect(x: 50, y: 100, width: 100, height: 80)
    
    let renderer = UIGraphicsImageRenderer(size: sz)
    let iMaskImage = renderer.image { ctx in
        // fill with black (any color other than clear will do)
        ctx.cgContext.setFillColor(UIColor.black.cgColor)
        ctx.fill(CGRect(origin: .zero, size: sz))
        // draw the image in maskRect with .xor blendMode
        //  this will make all non-transparent pixels in the image transparent
        pImage.draw(in: maskRect, blendMode: .xor, alpha: 1.0)
    }

到时候iMaskImage就是这个(白色的“P”字不是白色的……是透明的):

然后您可以将该图像用作任何其他视图的蒙版。

这里有一个例子MaskedTriangleView——它使用CAShapeLayer作为三角形,然后使用“P”图像作为图层蒙版:

class MaskedTriangleView: UIView {
    
    let shapeLayer = CAShapeLayer()
    var maskImage: UIImage?

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        layer.addSublayer(shapeLayer)
        shapeLayer.fillColor = UIColor.systemYellow.cgColor
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 1
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // create a triangle shape path
        let bez: UIBezierPath = UIBezierPath()
        bez.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
        bez.addLine(to: CGPoint(x: bounds.midX, y: bounds.minY))
        bez.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        bez.close()
        shapeLayer.path = bez.cgPath
        
        if let img = maskImage {
            // let's make our mask image
            //  50% of the height of self
            let h: CGFloat = bounds.height * 0.5
            //  40% from the Top (leaving 10% space at the bottom)
            let y: CGFloat = bounds.height * 0.4
            //  keep it proportionally sized
            let ratio: CGFloat = h / img.size.height
            //  width is proportional to height
            let w: CGFloat = img.size.width * ratio
            //  center horizontally
            let x: CGFloat = (bounds.width - w) * 0.5
            
            // rect to draw the mask image
            let maskRect:CGRect = CGRect(x: x, y: y, width: w, height: h)
            
            let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(size: bounds.size)
            let iMaskImage: UIImage = renderer.image { ctx in
                // fill with black (any color other than clear will do)
                ctx.cgContext.setFillColor(UIColor.black.cgColor)
                ctx.fill(CGRect(origin: .zero, size: bounds.size))
                // draw the image in maskRect with .xor blendMode
                //  this will make all non-transparent pixels in the image transparent
                img.draw(in: maskRect, blendMode: .xor, alpha: 1.0)
            }
            // create a layer
            let maskLayer: CALayer = CALayer()
            // set the new image as its contents
            maskLayer.contents = iMaskImage.cgImage
            // same frame as self
            maskLayer.frame = bounds
            // use it as the mask for the shape layer
            shapeLayer.mask = maskLayer
        }
    }
    
}

它看起来像这样(顶部图像覆盖在图像视图上,底部图像添加为主视图的子视图,绿色背景显示):

这是示例控制器:

class ImageMaskingVC: UIViewController {
    
    var bkgImageView: UIImageView!
    var triangleView1: MaskedTriangleView!
    var triangleView2: MaskedTriangleView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGreen
        
        // make sure we can load the images
        guard let bkimg = UIImage(named: "samplePic") else {
            print("Could not load background image!")
            return
        }
        
        guard let pImage = UIImage(named: "pImage") else {
            print("Could not load mask image!")
            return
        }
        
        // create the image view and set its image
        bkgImageView = UIImageView()
        bkgImageView.image = bkimg
        
        // create a MaskedTriangleView and set its maskImage
        triangleView1 = MaskedTriangleView()
        triangleView1.maskImage = pImage
        
        // create another MaskedTriangleView and set its maskImage
        triangleView2 = MaskedTriangleView()
        triangleView2.maskImage = pImage
        
        // add the views
        [bkgImageView, triangleView1, triangleView2].forEach {
            if let v = [=12=] {
                v.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(v)
            }
        }

        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain background image view
            //  Top / Leading / Trailing at 20-pts
            bkgImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            bkgImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            bkgImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            
            // height proportional to background image size
            bkgImageView.heightAnchor.constraint(equalTo: bkgImageView.widthAnchor, multiplier: bkimg.size.height / bkimg.size.width),

            // constrain first MaskedTriangleView exactly on top of the background image view
            triangleView1.topAnchor.constraint(equalTo: bkgImageView.topAnchor, constant: 20.0),
            triangleView1.leadingAnchor.constraint(equalTo: bkgImageView.leadingAnchor, constant: 20.0),
            triangleView1.trailingAnchor.constraint(equalTo: bkgImageView.trailingAnchor, constant: -20.0),
            triangleView1.bottomAnchor.constraint(equalTo: bkgImageView.bottomAnchor, constant: -20.0),

            // constrain the second MaskedTriangleView below the background image view
            //  with same width and height as the first MaskedTriangleView
            triangleView2.topAnchor.constraint(equalTo: bkgImageView.bottomAnchor, constant: 20.0),
            triangleView2.widthAnchor.constraint(equalTo: triangleView1.widthAnchor, constant: 0.0),
            triangleView2.heightAnchor.constraint(equalTo: triangleView1.heightAnchor, constant: 0.0),
            
            // centered horizontally
            triangleView2.centerXAnchor.constraint(equalTo: triangleView1.centerXAnchor, constant: 0.0),

        ])
        
    }
    
}