仅使 UIBezierPath 按钮内的点可选

make only points within UIBezierPath button selectable

我的 swift 代码具有一个由 UIBezierPath 制作的自定义形状按钮。如果用户触摸红色部分,则代码应仅调用 func press 如果用户触摸绿色部分,则不应调用 func press。现在,如果您触摸绿色部分,函数仍会被调用。

import UIKit

class ViewController: UIViewController {
    let customButton = UIButton(frame: CGRect(x: 100.0, y: 100.0, width: 200.0, height: 140.0))
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        customButton.backgroundColor = UIColor.green
        
        let aPath = UIBezierPath()
        
        aPath.move(to: CGPoint(x: 50, y: 0))
        
        aPath.addLine(to: CGPoint(x: 200.0, y: 40.0))
        aPath.addLine(to: CGPoint(x: 160, y: 140))
        aPath.addLine(to: CGPoint(x: 40.0, y: 140))
        aPath.addLine(to: CGPoint(x: 0.0, y: 40.0))
        
        aPath.close()
        
        let layer = CAShapeLayer()
        layer.fillColor = UIColor.red.cgColor
        layer.strokeColor = UIColor.red.cgColor
        layer.path = aPath.cgPath
        
        customButton.layer.addSublayer(layer)
        
        self.view.addSubview(customButton)
        customButton.addTarget(self, action: #selector(press), for: .touchDown)
        
    }
    
    @objc func press(){
        print("hit")
    }
    
    
}

首先要为您的自定义按钮添加您的约束,以便试图解决问题的社区能够轻松识别您的问题。例如;

   customButton.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                customButton.widthAnchor.constraint(equalToConstant: 250),
                customButton.heightAnchor.constraint(equalToConstant: 100),
                customButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
                customButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
            ])

你的问题出在你的代码中,你正在向你的图层添加填充,而你的图层已经存在于你的按钮本身中。所以无法识别你的事件响应者,你的功能自然就不起作用了。

yourGreenPartWhichYouClickable.isUserInteractionEnabled = false
yourRedPartWhichYouClickable.isUserInteractionEnabled = false

我的解决方案是将 UIButton 子类化为 FunkyButton。该按钮包含路径信息。 FunkyButton 覆盖 hitTest(_:event:) 方法,并检查该点是否包含在路径中。
试一试:

class ViewController: UIViewController {
    let customButton = FunkyButton(frame: CGRect(x: 100.0, y: 100.0, width: 200.0, height: 140.0))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        customButton.backgroundColor = UIColor.green

        self.view.addSubview(customButton)
        customButton.addTarget(self, action: #selector(press), for: .touchDown)        
    }
    
    @objc func press(){
        print("hit")
    }

}

class FunkyButton: UIButton {
    let aPath = UIBezierPath()

    override init (frame : CGRect) {
        super.init(frame : frame)
        aPath.move(to: CGPoint(x: 50, y: 0))

        aPath.addLine(to: CGPoint(x: 200.0, y: 40.0))
        aPath.addLine(to: CGPoint(x: 160, y: 140))
        aPath.addLine(to: CGPoint(x: 40.0, y: 140))
        aPath.addLine(to: CGPoint(x: 0.0, y: 40.0))

        aPath.close()
        let shapedLayer = CAShapeLayer()
        shapedLayer.fillColor = UIColor.red.cgColor
        shapedLayer.strokeColor = UIColor.red.cgColor
        shapedLayer.path = aPath.cgPath

        layer.addSublayer(shapedLayer)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.isHidden == true || self.alpha < 0.1 || self.isUserInteractionEnabled == false {
            return nil
        }
        if aPath.contains(point) {
            return self
        }
        return nil
    }
}