向 CAGradientLayer 添加蒙版使 UIBezierPath 消失
Posted
技术标签:
【中文标题】向 CAGradientLayer 添加蒙版使 UIBezierPath 消失【英文标题】:Adding a mask to CAGradientLayer makes UIBezierPath disappear 【发布时间】:2017-12-20 02:09:02 【问题描述】:我想为带有渐变的视图添加内边框。以下代码有效并给了我这个结果
import UIKit
class InnerGradientBorderView: UIView
override init(frame: CGRect)
super.init(frame: frame)
backgroundColor = UIColor.clear
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
backgroundColor = UIColor.clear
override func draw(_ rect: CGRect)
super.draw(rect)
addGradientInnerBorder(width: 8, color: FlatWhite())
func addGradientInnerBorder(width: CGFloat, color: UIColor)
// Setup
let topLeftO = CGPoint(x: 0, y: 0)
let topLeftI = CGPoint(x: width, y: width)
let topRightO = CGPoint(x: frame.width, y: 0)
let topRightI = CGPoint(x: frame.width - width, y: width)
let bottomLeftO = CGPoint(x: 0, y: frame.height)
let bottomLeftI = CGPoint(x: width, y: frame.height - width)
let bottomRightO = CGPoint(x: frame.width, y: frame.height)
let bottomRightI = CGPoint(x: frame.width - width, y: frame.height - width)
// Top
let topPoints = [topLeftO, topLeftI, topRightI, topRightO, topLeftO]
let topGradientPoints = [CGPoint(x: 0, y: 0), CGPoint(x: 0, y: 1)]
addGradientToBeizerPath(path: addClosedPathForPoints(points: topPoints), color: color, gradientPoints: topGradientPoints)
// Left
let leftPoints = [topLeftO, topLeftI, bottomLeftI, bottomLeftO, topLeftO]
let leftGradientPoints = [CGPoint(x: 0, y: 0), CGPoint(x: 1, y: 0)]
addGradientToBeizerPath(path: addClosedPathForPoints(points: leftPoints), color: color, gradientPoints: leftGradientPoints)
// Right
let rightPoints = [topRightO, topRightI, bottomRightI, bottomRightO, topRightO]
let rightGradientPoints = [CGPoint(x: 1, y: 0), CGPoint(x: 0, y: 0)]
addGradientToBeizerPath(path: addClosedPathForPoints(points: rightPoints), color: color, gradientPoints: rightGradientPoints)
// Bottom
let bottomPoints = [bottomLeftO, bottomLeftI, bottomRightI, bottomRightO, bottomLeftO]
let bottomGradientPoints = [CGPoint(x: 0, y: 1), CGPoint(x: 0, y: 0)]
addGradientToBeizerPath(path: addClosedPathForPoints(points: bottomPoints), color: color, gradientPoints: bottomGradientPoints)
func addClosedPathForPoints(points: [CGPoint]) -> UIBezierPath?
guard points.count == 5 else return nil
let path = UIBezierPath()
path.move(to: points[0])
path.addLine(to: points[1])
path.addLine(to: points[2])
path.addLine(to: points[3])
path.addLine(to: points[4])
path.close()
return path
func addGradientToBeizerPath(path: UIBezierPath?, color: UIColor, gradientPoints: [CGPoint])
guard let path = path, gradientPoints.count == 2 else return
let gradient = CAGradientLayer()
gradient.frame = path.bounds
gradient.colors = [color.cgColor, UIColor.clear.cgColor]
gradient.startPoint = gradientPoints[0]
gradient.endPoint = gradientPoints[1]
// let shapeMask = CAShapeLayer()
// shapeMask.path = path.cgPath
// gradient.mask = shapeMask
self.layer.insertSublayer(gradient, at: 0)
你会注意到边缘看起来不太好。为了解决这个问题,我给边缘一个角度。当我用这个角度对这个渐变应用蒙版时,右边和底部的路径会像这样消失:
我在这里所做的只是使用一些封闭的 bezierPaths 并对它们应用渐变。如果渐变有遮罩(注释代码未注释),则其中两条路径消失。我有一种不明白的感觉,所以希望这里有人能告诉我如何正确使用 CAShapeLayer。
【问题讨论】:
我不明白您为什么要从draw
方法内部将 CAGradientLayer
添加到视图的 layer
- 这对我来说感觉不对。你能解释一下你想要达到的目标吗?
我添加这个是可调整大小的。而且由于每当需要绘制或重绘视图时都会调用draw
方法,所以我想我会在那里完成工作。您有更好的实施方案吗?
是的,layoutSubviews
或其他地方。您应该只覆盖draw
来实际执行渲染,而不是更改层层次结构。由于您通过draw
调用插入图层,并且从不删除我可以看到的图层,因此您最终会得到数百个图层,不是吗?我实际上很难理解你想要实现的目标......
我明白你的意思。不知道我在想什么。让我通过代码审查来解决这个问题,我将用更有意义的代码更新我的问题。感谢您的回复。
【参考方案1】:
This comment 到 CALayer mask property 完美解释:
遮罩层位于被遮罩层的坐标系中,就像它是子层一样。
在您的情况下,右侧和底部渐变层的原点不是
在封闭视图的(0, 0)
,但在(frame.width - width, 0)
和(frame.height - width, 0)
分别。
另一方面,点的坐标
oshapeMask.path
相对于封闭视图的(0, 0)
。
一个可能的简单解决方法是转换坐标系 形状图层,使其使用与点相同的坐标 给定路径的:
let gradient = CAGradientLayer()
gradient.frame = path.bounds
gradient.bounds = path.bounds // <<--- ADDED HERE!
gradient.colors = [color.cgColor, UIColor.clear.cgColor]
gradient.startPoint = gradientPoints[0]
gradient.endPoint = gradientPoints[1]
let shapeMask = CAShapeLayer()
shapeMask.path = path.cgPath
gradient.mask = shapeMask
self.layer.addSublayer(gradient)
【讨论】:
这解决了这个问题。谢谢!以上是关于向 CAGradientLayer 添加蒙版使 UIBezierPath 消失的主要内容,如果未能解决你的问题,请参考以下文章
CAGradientLayer 使用 UIScrollView 滚动