Event handling for iOS - how hitTestwithEvent and pointInsidewithEvent are related?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
hitTest:withEvent: and pointInside:withEvent: are part of the same touch-routing process, but they do different jobs. pointInside answers a simple yes-or-no question about whether a touch point is inside a view's interactive area. hitTest uses that information while walking the view hierarchy to decide which view should actually receive the event.
The Basic Relationship
When a touch arrives, UIKit starts high in the view tree and asks views whether the touch could belong to them. A view that is hidden, nearly transparent, or not interactive is usually ignored immediately.
At a conceptual level, the flow looks like this:
- UIKit calls
hitTest:withEvent:on a parent view. - That method checks whether the view is eligible to receive touches.
- As part of that process, it calls
pointInside:withEvent:on the same view. - If the point is outside, the view returns
nil. - If the point is inside,
hitTestcontinues by asking subviews, usually from frontmost to backmost.
That is why pointInside is best thought of as a filter and hitTest as the chooser.
A Modern Swift Example
The current Swift method names are point(inside:with:) and hitTest(_:with:), but they map directly to the older Objective-C names in the article title.
Here, you override point(inside:with:) to make the tappable area larger without changing the button's visible frame. You do not need to override hitTest for that use case because the default hit-testing logic will use your new inside-area rule automatically.
When to Override hitTest
Override hitTest when you want to reroute or customize which subview wins the event, not just whether a point counts as inside the current view.
This pattern creates an overlay that lets touches pass through empty parts of the overlay while still allowing interactive subviews to receive touches.
How the Two Methods Work Together
The default implementation of hitTest typically checks the current view first, then converts the touch point into each subview's coordinate system and asks those subviews in reverse order. Reverse order matters because frontmost views should usually win the hit test.
If pointInside returns false, the view is not a candidate and its subviews are not considered through that branch. That is why a custom pointInside implementation can dramatically change touch behavior even when you never override hitTest directly.
Common Pitfalls
The most common mistake is overriding hitTest when pointInside would have been enough. If all you need is a larger or smaller touch area, changing the inside check is simpler and safer.
Another mistake is forgetting coordinate systems. The point passed into each method is already expressed in that view's local coordinates, so you should not compare it to another view's frame without conversion.
Be careful when returning nil from hitTest. That can make controls feel untappable if you accidentally discard a legitimate target view.
Finally, remember that gesture recognizers and control events sit on top of hit testing. If the wrong view wins the hit test, everything built on top of it becomes harder to reason about.
Summary
- '
pointInside:withEvent:answers whether a touch point belongs inside a view's touchable region.' - '
hitTest:withEvent:uses that answer while selecting the final target view.' - Override
pointInsideto change touch area size. - Override
hitTestonly when you need to reroute event delivery. - Keep coordinate systems and view ordering in mind when debugging touch behavior.

