使用遮罩从 UIView 切出一个圆圈 [英] Cut a circle out of a UIView using mask

查看:29
本文介绍了使用遮罩从 UIView 切出一个圆圈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我有一个方形 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 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.

这篇关于使用遮罩从 UIView 切出一个圆圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆