且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用遮罩从 UIView 切出一个圆圈

更新时间:2023-11-04 09:39:58

我建议为你的面具画一条路径,例如在 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 the frame 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.