使用蒙版从UIView中剪出一个圆圈

时间:2016-11-03 17:16:46

标签: swift xcode

在我的应用程序中,我有一个方形UIView,我想从顶部切出一个洞/缺口。在线的所有教程都是一样的,看起来很简单,但是每一个教程总是与我想要的完全相反。

例如,这是自定义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,深蓝色是背景):

What I want

但这是我得到的。 (无论我尝试什么,每一次)

What I get

我不确定如何实现这一点,除了制作一个已经是我需要的确切形状的面具。但如果我能够做到这一点,那么我首先就不会遇到这个问题。有没有人知道如何实现这个目标?

编辑:这个问题应该是我已经尝试过并且无法正常工作的问题。也许我做错了或在错误的环境中使用它。我不熟悉任何给定的方法,并且使用指针使它看起来有点过时。接受的答案可以更好地解释如何使用更广泛使用的UIBezierPaths以及在自定义UIView的上下文中实现这一点。

1 个答案:

答案 0 :(得分:6)

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

注意,我调整了这个以在两个地方设置掩码:

  • 来自layoutSubviews:这样,如果框架发生变化,例如自动布局(或通过手动更改frame或其他),它会相应更新;以及

  • 如果您更新radius:这样,如果您在故事板中使用此功能,或者以编程方式更改半径,则会反映出此更改。

所以,你可以在深蓝色BottomOverlayView上覆盖半高,浅蓝色UIView,如下所示:

enter image description here

产量:

enter image description here

如果你想使用"切一个洞"在重复答案中建议的技术,updateMask方法将是:

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
}

我个人发现路径中的奇偶规则路径有点违反直觉。在我可以的地方(比如这种情况),我只想画出面具的路径。但是如果你需要一个带有切口的面具,这种偶数填充规则方法会很有用。

相关问题