Custom Shape UIButton Tutorial

Josip on

blog-custom-buttons-img1

By default, the buttons on iOS are rectangular shaped. In most situations in which a developer can be that’s enough and we can do most of the things.

But sometimes, application requirements are such that we need a different approach to resolve that situation. Let’s say you need the area of the button to be a circle or a triangle, the rectangular approach can’t be used here.

Problem

Let’s imagine that we need the UI shown on image. We have 8 buttons where every button is a triangle. Every button is represented by a different color and when someone clicks on a color we will present that color.

With classic UIButton we don’t have a way to do this, buttons will overlap and it will be difficult to do what we want.

Solution

Our approach to resolving this is the UIButton subclass. There is a great method:

– (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

defined on UIView which we will override in our subclass. The basic idea is very simple: we will return button if touch point is inside the area we are interested in, otherwise we will return nil which means that we don’t want to receive the touch event and it will be forwarded further if someone else is interested to receive it.

Note that this method is defined on UIView class which means that we are not limited on UIButton but all UIView subclasses.

Technicalities

Now we know what we want and how to do it. It’s now a question of how exactly we will implement this. The way we will do this is:

  • Insert 8 buttons in xib file, one button for each area.
  • Insert outlets and create an array of those buttons. We will pass the array to the utility class.
  • Create utility class in which we will put methods related to button shapes.
  • In the utility class create three arrays of points. The first array will contain the first point of triangle for each button. The second array will contain the second point of triangle for each button, and finally the third array will contain the third point of each triangle.
  • CGPoint points1[8] = {{0, 0}, {70, 30}, {100, 0}, {70, 70},
    	{100, 100}, {30, 70}, {0, 100}, {30, 30}};
    CGPoint points2[8] = {{70, 30}, {100, 100}, {70, 70}, {0, 100},
    	{30, 70}, {0, 0}, {30, 30}, {100, 0}};
    CGPoint points3[8] = {{0, 100}, {0, 100}, {0, 0}, {0, 0},
    	{100, 0}, {100, 0}, {100, 100}, {100, 100}};
    
  • Also, in the utility class create a method which returns UIBezierPath triangle for each button. Like this:
  • UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:point1];
    [bezierPath addLineToPoint:point2];
    [bezierPath addLineToPoint:point3];
    [bezierPath closePath];
    
  • In the hitTest method check if the touch point is inside the triangle UIBezierPath for that button
  • If yes return that button otherwise return nil
  • - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    	UIBezierPath *path = [[PNTUtility sharedUtility]
    		bezierPathForButton:self];
        	if ([path containsPoint:point]) {
                    return self;
        	} else {
        		return nil;
        	}
    }
    
  • Of course, this steps depend on your application and how do you want and need to implement this. But you get the general idea.

You can check the example project on our github repo: https://github.com/Planet1107/CustomShapedButtons/

Conclusion

We mentioned that we are not limited to UIButton nor the triangles. You can create whichever shapes you want. The only thing that needs to be changed are the points that will define your shape. So next time when your designer has a “great idea” that will complicate your life, maybe this will help you. :)

Leave Your Comment

You’re more than welcome to leave your own comment!

Just please, pretty please: don’t spam. Thank you :)