使用Swift和CAShapeLayer()进行掩码操作时,如何避免在掩码区域相交时反转掩码?

3
这个问题很难表述,但进一步解释情况应该会有所帮助。
使用下面的代码,我基本上在屏幕上任何位置点击时都会遮盖一个圆形,以显示黑色UIView下面的内容。每当我点击时,我记录CGPoint并将其存储在数组中以跟踪被点击的位置。对于每次后续点击,我都会删除黑色UIView,并重新创建我正在跟踪的CGPoint数组中的每个已点击的点,以创建包括所有先前点的新掩码。
结果类似于这样:

Tapped regions

我相信你已经看出我在问什么了...我怎样才能避免口罩在圆圈相交的地方翻转?感谢你的帮助!
以下是我的代码供参考:
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
        }
}
1个回答

5
您的问题是:
如何避免掩膜相交时反转掩膜?
简而言之,不要使用.evenOdd填充规则。
您指定了一个fillRule.evenOdd。这将导致路径交叉反转。这是一个红色的视图,其遮罩由两个重叠的圆弧路径组成,使用.evenOdd规则:

enter image description here

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

enter image description here


例如。
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
    }
}

导致结果:

enter image description here


谢谢!这让我更接近了。在path.addArc()之后,我不得不添加path.close(),但它们现在确实合并了。然而,通过这种方法,我失去了在黑屏上“切割透明孔洞”的整个效果。现在它根本没有添加黑屏,而是添加了颜色与overlayView.backgroundColor设置相同的圆圈。最终,我希望屏幕是黑色的,并在点击点处切割透明孔洞以便看到背后的视图。我还在继续尝试,希望能找到解决方法 :) - undefined
唉,我还是搞不明白。看起来,我正在创建的新视图开始时是不可见的,而我创建的圆圈则使这些区域可见,但我需要相反的效果。希望视图一开始就是可见的,然后通过我的圆圈使其变得不可见。 - undefined
如果你想展示图像视图的圆形部分,在黑色“覆盖”视图的上方放置图像视图,并将整个图像视图进行遮罩处理。然后,当用户点击时,用一个带有弧线的CAShapeLayer替换图像视图的遮罩,以揭示黑色视图前方的图像视图部分。但根据你在问题中分享的图片,我假设你已经在这样做了... - undefined
1
这里的技巧是你有一个带有实心填充的单一路径,但你无法为每个子路径(每个圆)应用单独的渐变,我猜这是你的目标。你可能需要一个非常不同(而且更复杂)的方法。也许可以使用两个独立的图像视图,其中一个视图下面是一个不透明的黑色图像,你可以更新其像素缓冲区以在小圆形图案中更新 alpha 通道。也许你也可以使用自定义的 CALayer,但这对我来说并不明显。很遗憾,这超出了这个问题的范围。 - undefined
谢谢,Rob!我通过使用UIView的maskView属性成功完成了这个任务:https://developer.apple.com/documentation/uikit/uiview/1622557-maskview 这使我能够将CAGradientLayer作为UIView的子层,并使用该UIView来遮罩主图像视图。感谢你的建议! - undefined
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接