My swift code features a custom shape button made from a UIBezierPath. The code should only call the func press if it the user toches the red part if the user touches the green part the func press should not be called. Right now if you touch the green part the func is still called.
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")
}
}
My solution to the problem is to subclass UIButton
into a FunkyButton
. That button contains the path information. FunkyButton overrides the hitTest(_:event:)
method, and checks if the point is contained in the path.
Give this a whirl:
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
}
}