【问题标题】:Using Swift and CAShapeLayer() with masking, how can I avoid inverting the mask when masked regions intersect?使用带有遮罩的 Swift 和 CAShapeLayer(),当遮罩区域相交时,如何避免反转遮罩?
【发布时间】:2021-06-26 15:51:52
【问题描述】:

这个问题很难用言语表达,但进一步解释情况应该会有所帮助。

使用下面的代码,我实质上是在屏幕上屏蔽了一个圆圈,无论我在哪里点击以显示黑色 UIView 下方的内容。当我点击时,我将 CGPoint 记录在一个数组中以跟踪被点击的位置。对于我随后进行的每一次点击,我都会删除黑色 UIView 并从我正在跟踪的 CGPoints 数组中重新创建每个点击点,以便创建一个包含所有先前点的新蒙版。

结果是这样的:

我相信您已经知道我在问什么...如何避免蒙版在圆圈相交的地方反转?感谢您的帮助!

这是我的参考代码:

class MiniGameShadOViewController: UIViewController {
    
    //MARK: - DECLARATIONS
 
    var revealRadius : CGFloat = 50
    var tappedAreas : [CGPoint] = []

    //Objects
    @IBOutlet var shadedRegion: UIView!
    
    //Gesture Recognizers
    @IBOutlet var tapToReveal: UITapGestureRecognizer!
    
    

    //MARK: - VIEW STATES

    override func viewDidLoad() {
        super.viewDidLoad()

    }
    
  
    //MARK: - USER INTERACTIONS
    @IBAction func regionTapped(_ sender: UITapGestureRecognizer) {
        
        let tappedPoint = sender.location(in: view)
        
        tappedAreas.append(tappedPoint) //Hold a list of all previously tapped points
       
        //Clean up old overlays before adding the new one
        for subview in shadedRegion.subviews {
            if subview.accessibilityIdentifier != "Number" {subview.removeFromSuperview()}
        }
            //shadedRegion.layer.mask?.removeFromSuperlayer()
        
        createOverlay()
    }

    
    //MARK: - FUNCTIONS
    
    func createOverlay(){
        
        //Create the shroud that covers the orbs on the screen
        let overlayView = UIView(frame: shadedRegion.bounds)
            overlayView.alpha = 1
            overlayView.backgroundColor = UIColor.black
            overlayView.isUserInteractionEnabled = false
        shadedRegion.addSubview(overlayView)
        
        let path = CGMutablePath()
        
        //Create the box that represents the inverse/negative area relative to the circles
        path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))

        //For each point tapped so far, create a circle there
        for point in tappedAreas {
            path.addArc(center: point, radius: revealRadius, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: false)
            path.closeSubpath() //This is required to prevent all circles from being joined together with lines
          }
        
            //Fill each of my circles
            let maskLayer = CAShapeLayer()
                maskLayer.backgroundColor = UIColor.black.cgColor
                maskLayer.path = path;
                maskLayer.fillRule = .evenOdd
            
            //Cut out the circles inside that box
            overlayView.layer.mask = maskLayer
            overlayView.clipsToBounds = true
        }
}

【问题讨论】:

    标签: swift overlay mask overlap cashapelayer


    【解决方案1】:

    你问:

    当蒙版区域相交时,如何避免反转蒙版?

    简而言之,不要使用.evenOdd 填充规则。


    您指定了 fillRule.evenOdd。这导致要反转的路径的交叉点。这是一个带有遮罩的红色视图,该遮罩由一条带有两条重叠圆弧的路径组成,带有.evenOdd 规则:

    如果您使用.nonZero(巧合的是,这是形状图层的默认填充规则),它们将不会相互反转:


    例如

    class ViewController: UIViewController {
        @IBOutlet weak var imageView: UIImageView!
        
        var maskLayer: CAShapeLayer = {
            let shapeLayer = CAShapeLayer()
            shapeLayer.fillColor = UIColor.white.cgColor
            return shapeLayer
        }()
    
        var points: [CGPoint] = [] // this isn't strictly necessary, but just in case you want an array of the points that were tapped
        var path = UIBezierPath()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            imageView.layer.mask = maskLayer
        }
    
        @IBAction func handleTapGesture(_ gesture: UITapGestureRecognizer) {
            let point = gesture.location(in: gesture.view)
            points.append(point)
    
            path.move(to: point)
            path.addArc(withCenter: point, radius: 40, startAngle: 0, endAngle: .pi * 2, clockwise: true)
    
            maskLayer.path = path.cgPath
        }
    }
    

    导致:

    【讨论】:

    • 谢谢!这让我更接近了。我必须在path.addArc() 之后添加path.close(),但它们现在肯定正在合并。但是,我确实失去了这条路线的整个“黑屏切割孔”方面。现在它根本没有添加黑屏,而是添加了我设置overlayView.backgroundColor 的彩色圆圈。最终,我希望屏幕是黑色的,并在点击点处切出透明的孔,以便看到它后面的视图。不过,我仍然在玩弄它,所以我希望能偶然发现它:)
    • 呃,我仍然无法得到它。看来,我正在创建的要遮罩的新视图开始时是不可见的,而我正在创建的圆圈正在使这些区域可见,但我需要相反的情况。从视图可见开始,我的圈子使其不可见。
    • 如果您想显示图像视图的圆圈,请将图像视图置于黑色“覆盖”视图之上并遮盖整个图像视图。然后在用户点击时,用带有这些弧线的CAShapeLayer 替换图像视图的掩码,从而显示在黑色视图前面的图像视图部分。但是从您在问题中分享的图片来看,我认为您已经在这样做了……
    • 哇,这太棒了,而且代码也比我最终得到的要少。工作精美!它在我的图像中看起来正确的原因是使用了.evenodd 填充。它给出了我想要的相同结果,除了相交圆问题,你的解决方案完美地解决了这个问题,甚至不需要我以前试图跳过的额外箍哈哈。谢谢!
    猜你喜欢
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 2010-12-19
    • 1970-01-01
    • 2017-07-05
    相关资源
    最近更新 更多