✍️ Note
Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts
hitTest
Returns the farthest descendant in the view hierarchy of the current view, including itself, that contains the specified point.
Return value
- The view object that’s the farthest descendent of the current view and contains
point. Returnsnilif the point lies completely outside the view hierarchy of the current view.This method traverses the view hierarchy by calling the
point(inside:with:)method of each subview to determine which subview to send a touch event to. Ifpoint(inside:with:)returnstrue, this method continues to traverse the subview hierarchy until it finds the frontmost view that contains the specified point. If a view doesn’t contain the point, this method ignores its branch of the view hierarchy. You rarely need to call this method yourself, but you might override it to hide touch events from subviews.This method ignores view objects that are hidden, that have disabled user interactions, or that have an alpha level less than
0.01. This method doesn’t take the view’s content into account when determining a hit, so it can return a view even if the specified point is in a transparent portion of that view’s content.This method doesn’t report points that lie outside the view’s bounds as hits, even if they actually lie within one of the view’s subviews. This situation can occur if the view’s
clipsToBoundsproperty isfalseand the affected subview extends beyond the view’s bounds.Note
When the system calls this method to perform hit-testing for event routing, it expects the resulting view to be part of a
https://developer.apple.com/documentation/uikit/uiview/1622469-hittestUIWindowhierarchy.
Let’s check How it works!

RedView is the farthest view in view hierarchy.
class ViewController: UIViewController {
@IBOutlet weak var blueView: UIView!
@IBOutlet weak var greenView: UIView!
@IBOutlet weak var redView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap))
view.addGestureRecognizer(tapGesture)
}
@objc func didTap(_ recognizer: UITapGestureRecognizer) {
let point = recognizer.location(in: view)
let farthestView = view.hitTest(point, with: nil)
let viewTag = farthestView?.tag
guard let viewTag else {
return
}
switch viewTag {
case 0:
print("BlueView")
case 1:
print("GreenView")
case 2:
print("RedView")
default:
break
}
}
}
When you tap the center of rectangle, It prints RedView because It’s the farthest view.

point(inside:with:)
Returns a Boolean value indicating whether the receiver contains the specified point. by Apple Document
point
- A point that is in the receiver’s local coordinate system (bounds).
eventThe event that warranted a call to this method. If you are calling this method from outside your event-handling code, you may specify
nil.
Let’s test.
@objc func didTap(_ recognizer: UITapGestureRecognizer) {
let point = recognizer.location(in: view)
let isPointInsideBlueView = blueView.point(inside: point, with: nil) //false
let isPointInsideGreenView = greenView.point(inside: point, with: nil) //false
let isPointInsideRedView = redView.point(inside: point, with: nil) //false
print(point)
print(blueView.bounds)
}

I changed UIView(Blue, Green, and Red) to CustomView to check hitTest and point(inside: CGPoint, with: event)

In fact, when pass the UITapGestureRecognizer’s point which is view based coordinates converted to subview’s coordinates.
//Point: UITapGestureRecognizer's point based on viewController's view
let point = recognizer.location(in: view)
//convert it to redView's coordinates (bounds)
let convertedPoint = view.convert(point, to: redView)

Summary
hitTest itself calls point(inside:) function to check is bounds contains point or not. It uses Depth First Search.
If
https://developer.apple.com/documentation/uikit/uiview/1622469-hittestpoint(inside:with:)returnstrue, this method continues to traverse the subview hierarchy until it finds the frontmost view that contains the specified point.

Leave a comment