You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CALayer中裁剪特定区域?手势绘制圆形裁剪问题求助

问题分析与解决方案

你的代码当前只能生成单个圆形的核心原因是:每次触发手势时,你都创建了一个全新的CAShapeLayer作为mask覆盖掉了原来的mask,而不是在已有路径上累积添加新的圆形区域。要实现手指移动时持续裁剪出圆形的绘制效果,我们需要复用同一个路径来记录所有绘制的圆形,而不是每次都重置mask。

修改步骤与完整代码

1. 持久化累积路径

在CustomImageView中添加一个CGMutablePath属性,用来保存所有绘制的圆形路径,这样每次手势触发时都能在已有路径上追加新的圆形。

2. 复用Mask Layer

不再每次创建新的CAShapeLayer,而是初始化一次后,只更新它的path属性。

3. 修正逻辑细节

调整mask的填充规则和路径构建逻辑,确保每次添加的圆形都会从遮罩层中“挖空”出来。

以下是修正后的完整代码:

class CustomImageView: UIImageView { 
    private var recognizer: UIPanGestureRecognizer! 
    private var maskLayer: CALayer! 
    // 新增:用来累积所有绘制圆形的路径
    private var maskPath: CGMutablePath!
    
    override init(frame: CGRect) { 
        super.init(frame: frame) 
        setup() 
    } 
    
    private func setup() { 
        self.isUserInteractionEnabled = true 
        recognizer = UIPanGestureRecognizer(target: self, action: #selector(swipeGesture)) 
        self.addGestureRecognizer(recognizer) 
        
        maskLayer = CALayer() 
        maskLayer.backgroundColor = UIColor.black.withAlphaComponent(0.6).cgColor 
        maskLayer.frame = self.bounds 
        self.layer.insertSublayer(maskLayer, at: 0) 
        
        // 初始化累积路径
        maskPath = CGMutablePath()
        // 先添加整个视图的矩形路径,作为初始遮罩
        maskPath.addRect(self.bounds)
        
        // 创建初始的mask layer
        let shapeMask = CAShapeLayer()
        shapeMask.path = maskPath
        shapeMask.fillRule = .evenOdd
        maskLayer.mask = shapeMask
    } 
    
    required init?(coder aDecoder: NSCoder) { 
        super.init(coder: aDecoder) 
        setup() 
    } 
    
    // 修正拼写错误:crateMask → createMask
    private func createMask(location: CGPoint) { 
        let maskFrame = CGRect(origin: CGPoint(x: location.x - 25 , y: location.y - 25 ), size: CGSize(width: 50, height: 50)) 
        let cornerRadius = maskFrame.height / 2 
        // 在累积路径上添加新的圆形(圆角矩形)
        maskPath.addRoundedRect(in: maskFrame, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
        
        // 更新mask的path
        if let shapeMask = maskLayer.mask as? CAShapeLayer {
            shapeMask.path = maskPath
        }
    } 
    
    @objc func swipeGesture(sender: UIPanGestureRecognizer) { 
        let location = sender.location(in: self) 
        print(location) 
        createMask(location: location) 
    } 
}

关键逻辑解释

  • 累积路径maskPath:一开始先添加整个视图的矩形路径,之后每次手势触发时,在路径上追加新的圆形(圆角矩形)。
  • evenOdd填充规则:这个规则会让路径中重叠奇数次的区域被填充,偶数次的区域被镂空。我们先添加整个视图的矩形(填充遮罩),再添加圆形(相当于在遮罩上挖洞),这样每次添加的圆形区域都会变成透明,露出下方的内容。
  • 复用CAShapeLayer:只在初始化时创建一次CAShapeLayer,之后只更新它的path属性,这样就能保留之前所有绘制的圆形,而不会被重置。

这样修改后,当你用手指在视图上滑动时,就会持续裁剪出一系列圆形,实现你想要的绘制效果了。

内容的提问来源于stack exchange,提问作者Vladimir Anisimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:18:25