b2cloud

5th November 2014

iOS view hit regions

Guides | Tutorial By 2 years ago

According to the Apple Human Interface Guidelines, the smallest point size a control should be in iOS is 44×44 points.

In some designs you will get really small controls which you need to still work at that size. A common way of doing this is to create an invisible UIButton at put it over your small control.

For one, this creates more code to maintain and is also way more work than what’s required.

There is actually a very simple way to do this, however it’s probably not the most well known API, so I don’t blame people for not knowing of its existence.

Overriding UIView’s -hitTest:withEvent: method call, you can specify whether a view should or should not take an event, even if the tap doesn’t intercept that views bounds.

For example, from a custom UIControl subclass, you can extend the frame to a minimum of 44×44 by adding this override

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
	const CGFloat minimumHitDimension = 44;
	const CGRect minimumHitRect = CGRectMake(self.bounds.size.width / 2 - minimumHitDimension / 2,
											 self.bounds.size.height / 2 - minimumHitDimension / 2,
											 minimumHitDimension,
											 minimumHitDimension);
	
	//	Hit rectangle = our bounds or the minimum rect if larger
	const CGRect hitRect = CGRectUnion(self.bounds, minimumHitRect);
	
	if(CGRectContainsPoint(hitRect, point))
	{
		return self;
	}
	
	return [super hitTest:point withEvent:event];
}

This can also be done from a container view, looking through its subviews and potentially modifying the hit target on a subview.

Hit rectangles are just the beginning. You can also do more complicated hit tests with your own code, or even the UIBezierPath.

This next example makes sure that hits are only accepted in an eclipse inside the view.

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
	const CGFloat cornerRadius = 50;
	UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius];
	
	//	Only allow us to hit if the point intercepts the bezier path
	return (([path containsPoint:point]) ? self : nil);
}
Recommended Posts

Dynamic UIAlertViews and UIActionSheets (and best practices)

Post by 2 years ago

Too often I see UIAlertViews and UIActionSheets being setup poorly. When adding more than 2 buttons, I see a lot of people in the delegate method hardcoding button indexes (if you find yourself hardcoding anything,...

Got an idea?

We help entrepreneurs, organizations and established brands from around
the country bring ideas to life. We would love to hear from you!