How to create custom form button with non-clickable rectangle area in swift?

613 Views Asked by At

Here is my Button class.

@IBDesignable
class Button: UIButton {
    var cornerRadii = CGSize()

    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            cornerRadii = CGSize(width: cornerRadius, height: cornerRadius)
        }
    }

    @IBInspectable var color: UIColor = .green

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: cornerRadii)
        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = self.layer.bounds
        shapeLayer.path = path.cgPath
        layer.mask = shapeLayer
        color.setFill()
        path.fill()
    }
}

So everything works pretty well. My button has a half-circe form so actually when I click at the empty corner of the button frame I do not expect it to be pressed, but it actually happens. I thought that mask can help me with this issue but I didn't manage do make it.

So the questing how I can create a button with two empty corners that are not clickable?

3

There are 3 best solutions below

0
Sasha Kozachuk On BEST ANSWER

Ovveride point inside method for your custom Button:

override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: cornerRadii)
    return path.contains(point)
}
0
PlusInfosys On

You can override whitest method and check if touch point is inside your bazier path or not. You can check path.containsPoint(point); to find if its inside. If its inside return self or nil.

0
Matic Oblak On

As from the comments you can override the hit test method on the button.

This works for me if you need to disable the touches outside the path:

class Button: UIButton {
    var cornerRadii = CGSize()

    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            cornerRadii = CGSize(width: cornerRadius, height: cornerRadius)
        }
    }

    @IBInspectable var color: UIColor = .green

    var currentPath: UIBezierPath?

    override func draw(_ rect: CGRect) {
        super.draw(rect)


        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: cornerRadii)
        currentPath = path // Save the path here to be used in hit test


        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = self.layer.bounds
        shapeLayer.path = path.cgPath
        layer.mask = shapeLayer
        color.setFill()
        path.fill()
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        // check if point is outside the given path and return nil in that case. Otherwise return super
        if currentPath?.contains(point) == false {
            return nil
        } else {
            return super.hitTest(point, with: event)
        }
    }
}