如何从 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),
])
}
}
我有一个看起来像这样的 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),
])
}
}