使用遮罩从 UIView 切出一个圆圈 [英] Cut a circle out of a UIView using mask
问题描述
在我的应用程序中,我有一个方形 UIView,我想从顶部切出一个孔/凹口.网上的所有教程都一样,看起来很简单,但每一个教程总是与我想要的完全相反.
In my app I have a square UIView and I want to cut a hole/notch out of the top of. All the tutorials online are all the same and seemed quite straightforward but every single one of them always delivered the exact opposite of what I wanted.
例如这是自定义 UIView 的代码:
For example this is the code for the custom UIView:
class BottomOverlayView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
drawCircle()
}
fileprivate func drawCircle(){
let circleRadius: CGFloat = 80
let topMidRectangle = CGRect(x: 0, y: 0, width: circleRadius*2, height: circleRadius*2)
let circle: CAShapeLayer = CAShapeLayer()
circle.position = CGPoint(x: (frame.width/2)-circleRadius, y: 0-circleRadius)
circle.fillColor = UIColor.black.cgColor
circle.path = UIBezierPath(ovalIn: topMidRectangle).cgPath
circle.fillRule = kCAFillRuleEvenOdd
self.layer.mask = circle
self.clipsToBounds = true
}
}
这里是我希望实现的(浅蓝色是UIView,深蓝色是背景):
Here is what I hope to achieve (the light blue is the UIView, the dark blue is the background):
但这是我得到的.(无论我尝试什么,每次)
But here is what I get instead. (Every single time no matter what I try)
我不知道如何实现这一点,除了制作一个已经是我需要的确切形状的面具.但如果我能够做到这一点,那么我一开始就不会遇到这个问题.有没有人有任何关于如何实现这一目标的提示?
I'm not sure how I would achieve this, aside from making a mask that is already the exact shape that I need. But if I was able to do that then I wouldn't be having this issue in the first place. Does anyone have any tips on how to achieve this?
这应该是我已经尝试过但无法开始工作的重复问题.也许我做错了或在错误的上下文中使用它.我不熟悉任何给定的方法,而且指针的使用使它看起来有点过时.接受的答案在解释如何使用更广泛使用的 UIBezierPaths 以及在自定义 UIView 的上下文中实现这一点方面做得更好.
The question that this is supposedly a duplicate of I had already attempted and was not able to get working. Perhaps I was doing it wrong or using it in the wrong context. I wasn't familiar with any of the given methods and also the use of pointers made it seem a bit outdated. The accepted answer does a much better job of explaining how this can be implemented using much more widely used UIBezierPaths and also within the context of a custom UIView.
推荐答案
我建议为你的面具画一条路径,例如在 Swift 3 中
I'd suggest drawing a path for your mask, e.g. in Swift 3
// BottomOverlayView.swift
import UIKit
@IBDesignable
class BottomOverlayView: UIView {
@IBInspectable
var radius: CGFloat = 100 { didSet { updateMask() } }
override func layoutSubviews() {
super.layoutSubviews()
updateMask()
}
private func updateMask() {
let path = UIBezierPath()
path.move(to: bounds.origin)
let center = CGPoint(x: bounds.midX, y: bounds.minY)
path.addArc(withCenter: center, radius: radius, startAngle: .pi, endAngle: 0, clockwise: false)
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.close()
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
注意,我对此进行了调整以在两个位置设置蒙版:
Note, I tweaked this to set the mask in two places:
来自
layoutSubviews
:这样,如果框架发生变化,例如由于自动布局(或通过手动更改frame
或其他方式),它将相应更新;和
From
layoutSubviews
: That way if the frame changes, for example as a result of auto layout (or by manually changing theframe
or whatever), it will update accordingly; and
如果您更新 radius
:这样,如果您在故事板中使用它或以编程方式更改半径,它将反映该更改.
If you update radius
: That way, if you're using this in a storyboard or if you change the radius programmatically, it will reflect that change.
因此,您可以在深蓝色 UIView
之上叠加半高的浅蓝色 BottomOverlayView
,如下所示:
So, you can overlay a half height, light blue BottomOverlayView
on top of a dark blue UIView
, like so:
结果:
如果您想使用重复答案中建议的切一个洞"技术,updateMask
方法将是:
If you wanted to use the "cut a hole" technique suggested in the duplicative answer, the updateMask
method would be:
private func updateMask() {
let center = CGPoint(x: bounds.midX, y: bounds.minY)
let path = UIBezierPath(rect: bounds)
path.addArc(withCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
let mask = CAShapeLayer()
mask.fillRule = .evenOdd
mask.path = path.cgPath
layer.mask = mask
}
我个人认为在具有奇偶规则的路径中的路径有点违反直觉.在我可以的地方(例如这种情况),我更喜欢只绘制蒙版的路径.但是,如果您需要一个有切口的遮罩,这种奇偶填充规则方法会很有用.
I personally find the path within a path with even-odd rule to be a bit counter-intuitive. Where I can (such as this case), I just prefer to just draw the path of the mask. But if you need a mask that has a cut-out, this even-odd fill rule approach can be useful.
这篇关于使用遮罩从 UIView 切出一个圆圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!