我想在tableView上应用模糊的UIEffectView
,但每个单元格中都有圆形的UIImageView
对象。我使用了一个蒙版,并修改了this answer中的解决方案,以创建一种方法来迭代地在每个单元格上方切出圆圈:
func cutCircle(inView view: UIView, rect1: CGRect, rect2: CGRect?) {
// Create new path and mask
let newMask = CAShapeLayer()
// Create path to clip
let newClipPath = UIBezierPath(rect: view.bounds)
let path1 = UIBezierPath(ovalIn: rect1)
newClipPath.append(path1)
if let rect2 = rect2 {
let path2 = UIBezierPath(ovalIn: rect2)
//FIXME: Need a way to get a union of both paths!
//newClipPath.append(path2)
}
// If view already has a mask
if let originalMask = view.layer.mask, let originalShape = originalMask as? CAShapeLayer, let originalPath = originalShape.path {
// Create bezierpath from original mask's path
let originalBezierPath = UIBezierPath(cgPath: originalPath)
// Append view's bounds to "reset" the mask path before we re-apply the original
newClipPath.append(UIBezierPath(rect: view.bounds))
// Combine new and original paths
newClipPath.append(originalBezierPath)
}
// Apply new mask
newMask.path = newClipPath.cgPath
newMask.fillRule = .evenOdd
view.layer.mask = newMask
}
在每个UIEffectView
上,使用以下命令在每个可见的表格单元格上调用此函数:for cell in tableView.visibleCells()
。它将新的圆圈添加到蒙版。
但是,某些项目的圆圈图标覆盖较小,如下所示:
我在上述方法中添加了第二个CGRect
参数,以有条件地切出该圆。但是,遮罩在两个圆圈重叠的地方保持不变,如下所示:
我在这里查看了一些答案,因为我需要找到一种方法来获取两个UIBezierPath
对象的并集。但是,事实证明这非常困难。我认为我不能使用绘图上下文,因为它是UIEffectView
,并且需要迭代地剪切蒙版。
我尝试更改填充规则(.evenOdd
,.nonZero
),但效果不理想。
有什么技巧可以将两个重叠的UIBezierPath
组合成一个蒙版?
总体目标是使用连续的表格单元格来实现这种效果,但是某些图标会带有多余的圆圈。
请注意,底部图标如何有多余的圆圈,但如何裁剪,而我目前的裁剪此多余圆圈的技术会导致上述问题,其中重叠部分没有按预期被掩盖。
答案 0 :(得分:1)
在您的函数中尝试以下代码
let newRect: CGRect
if let rect2 = rect2{
let raw = rect1.union(rect2)
let size = max(raw.width, raw.height)
newRect = CGRect(x: raw.minX, y: raw.minX, width: size, height: size)
}else{
newRect = rect1
}
let path1 = UIBezierPath(ovalIn: newRect)
newClipPath.append(path1)
全功能
func cutCircle(inView view: UIView, rect1: CGRect, rect2: CGRect?) {
// Create new path and mask
let newMask = CAShapeLayer()
// Create path to clip
let newClipPath = UIBezierPath(rect: view.bounds)
let newRect: CGRect
if let rect2 = rect2{
let raw = rect1.union(rect2)
let size = max(raw.width, raw.height) // getting the larger value in order to draw a proper circle
newRect = CGRect(x: raw.minX, y: raw.minX, width: size, height: size)
}else{
newRect = rect1
}
let path1 = UIBezierPath(ovalIn: newRect)
newClipPath.append(path1)
// If view already has a mask
if let originalMask = view.layer.mask, let originalShape = originalMask as? CAShapeLayer, let originalPath = originalShape.path {
// Create bezierpath from original mask's path
let originalBezierPath = UIBezierPath(cgPath: originalPath)
// Append view's bounds to "reset" the mask path before we re-apply the original
newClipPath.append(UIBezierPath(rect: view.bounds))
// Combine new and original paths
newClipPath.append(originalBezierPath)
}
// Apply new mask
newMask.path = newClipPath.cgPath
newMask.fillRule = .evenOdd
view.layer.mask = newMask
}
我使用内置函数union创建了一个原始CGRect,然后获得了最大值以绘制适当的圆。
答案 1 :(得分:1)
您可以使用 addArcWithCenter 方法将两个弧组合成所需的形状。例如:
#define DEGREES_TO_RADIANS(degrees)((M_PI * degrees)/180)
- (UIBezierPath*)createPath
{
UIBezierPath* path = [[UIBezierPath alloc]init];
[path addArcWithCenter:CGPointMake(25, 25) radius:25 startAngle:DEGREES_TO_RADIANS(30) endAngle:DEGREES_TO_RADIANS(60) clockwise:false];
[path addArcWithCenter:CGPointMake(40, 40) radius:10 startAngle:DEGREES_TO_RADIANS(330) endAngle:DEGREES_TO_RADIANS(120) clockwise:true];
[path closePath];
return path;
}
我无法找出确切的点,但是如果您可以调整常数,则可以创建所需的完美形状。
答案 2 :(得分:1)
由于已经接受了答案(来自用户Tibin Thomas),我能够使用UIBezierPath
修改弧的用法,以准确获得所需的内容。我已经接受了他的回答,但是在这里发布了我的最终代码以供将来参考。
值得注意的是,在调用此方法之前,我必须将CGRect
的坐标从UIImageView
的坐标转换为我的UIEffectView
的坐标空间。我还应用了-1的插图以获取1pt边框。因此,使用的半径比我的UIImageView
s的半径大1。
func cutCircle(inView view: UIView, rect1: CGRect, rect2: CGRect?) {
// Create new path and mask
let newMask = CAShapeLayer()
// Create path to clip
let newClipPath = UIBezierPath(rect: view.bounds)
// Center of rect1
let x1 = rect1.midX
let y1 = rect1.midY
let center1 = CGPoint(x: x1, y: y1)
// New path
let newPath: UIBezierPath
if let rect2 = rect2 {
// Need to get two arcs - main icon and padlock icon
// Center of rect2
let x2 = rect2.midX
let y2 = rect2.midY
let center2 = CGPoint(x: x2, y: y2)
// These values are hard-coded for 25pt radius main icon and bottom-right-aligned 10pt radius padlock icon with a 1pt additional border
newPath = UIBezierPath(arcCenter: center1, radius: 26, startAngle: 1.2, endAngle: 0.3, clockwise: true)
newPath.addArc(withCenter: center2, radius: 11, startAngle: 5.8, endAngle: 2.2, clockwise: true)
} else {
// Only single circle is needed
newPath = UIBezierPath(ovalIn: rect1)
}
newPath.close()
newClipPath.append(newPath)
// If view already has a mask
if let originalMask = view.layer.mask,
let originalShape = originalMask as? CAShapeLayer,
let originalPath = originalShape.path {
// Create bezierpath from original mask's path
let originalBezierPath = UIBezierPath(cgPath: originalPath)
// Append view's bounds to "reset" the mask path before we re-apply the original
newClipPath.append(UIBezierPath(rect: view.bounds))
// Combine new and original paths
newClipPath.append(originalBezierPath)
}
// Apply new mask
newMask.path = newClipPath.cgPath
newMask.fillRule = .evenOdd
view.layer.mask = newMask
}