Consider these are two button shapes without overlapping touch event how can i achive this shape?I want to do UIButton shape like exacly different images ex. If image if triangular button should looks like triangular.
I doing folowing it normally but not getting exact shape like image frame,
UIImage *btnmage = [UIImage imageNamed:@"triangular.png"];
UIButton *button = [[UIButton alloc]init];
[button setImage: btnImage forState:UIControlStateNormal];
Note: I published this code on GitHub.
This UIButton subclass will take a shape and use this for rendering and hit testing:
import UIKit
class ShapedButton: UIButton {
var shape: UIBezierPath = UIBezierPath() {
didSet{
let l = CAShapeLayer()
l.path = shape.cgPath
self.layer.mask = l
}
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return shape.cgPath.contains(point)
}
func configureUpFacingTriangle() {
let triangleBezier = UIBezierPath()
triangleBezier.move(to: CGPoint(x: self.frame.size.width / 2, y:0))
triangleBezier.addLine(to: CGPoint(x: 0, y: self.frame.size.height))
triangleBezier.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
triangleBezier.close()
self.shape = triangleBezier
let pointSize = self.titleLabel?.font.pointSize ?? 0
self.titleEdgeInsets.top = self.titleEdgeInsets.top + (self.frame.size.height - pointSize - 7)
}
func configureDownFacingTriangle() {
let triangleBezier = UIBezierPath()
triangleBezier.move(to: CGPoint.zero)
triangleBezier.addLine(to: CGPoint(x: self.frame.size.width, y: 0))
triangleBezier.addLine(to: CGPoint(x:self.frame.size.width/2, y: self.frame.size.height ))
triangleBezier.close()
let pointSize = self.titleLabel?.font.pointSize ?? 0
self.titleEdgeInsets.top = self.titleEdgeInsets.bottom - (self.frame.size.height - pointSize - 7)
self.shape = triangleBezier
}
}
use it like
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var leftButton: ShapedButton!
@IBOutlet weak var button: ShapedButton!
@IBOutlet weak var rightButton: ShapedButton!
override func viewDidLoad() {
super.viewDidLoad()
for (idx, b) in [leftButton, button, rightButton].enumerated() {
if let b = b {
if idx % 2 == 0 {
b.configureDownFacingTriangle()
} else {
b.configureUpFacingTriangle()
}
}
}
}
}
storyboard:
result:
Although the buttons overlap, they don't consume the touch events for the other buttons, as they will only successfully hit test it, if they occur inside of the shape.
Note: I changed the code to add everything but point(inside:with:)
to UIButton via an extension. This allows regular UIButton to be configured as an triangle, circle or any other shape. Though only an instance of ShapedButton will use the shape in hit testing, as it needs the overriden point(inside:with:)
. UIButton just will test the bounding box (aka: bounds). See the changes on GitHub.