My app's window has a splitview with three panes; the leftmost pane hosts a view-based outline view.
When I click somewhere on that outline view, I want to determine the bottom-most subview in the hierarchy that was clicked. That is, which row, which cell, and ultimately which subview (control) of the cell was clicked.
I tried overriding mouseDown(with:)
on the hosting view controller, but it isn't called; I suspected the outline view somehow is hogging the event so instead I decided to override the method directly on my NSOutlineView
subclass itself. This works. However, from there I can not go any further down the hierarchy than the outline view itself:
class MyOutlineView: NSOutlineView {
override func mouseDown(with event: NSEvent) {
if let view = self.window?.contentView?.hitTest(event.locationInWindow) {
Swift.print("\(view.className)")
}
}
...prints:
MyModule. MyOutlineView
The documentation for hittest()
reads:
Returns the farthest descendant of the view in the view hierarchy (including itself) that contains a specified point, or nil if that point lies completely outside the view. This method is used primarily by an NSWindow object to determine which view should receive a mouse-down event. You’d rarely need to invoke this method, but you might want to override it to have a view object hide mouse-down events from its subviews. This method ignores hidden views.
How can I hit-test the topmost subviews of the outline view?
mouseDown(with:)
is called on views, not on controllers.
Usually the control inside the outline view handles the mouseDown.
But if you want to find the control, yes, -[NSTableView(NSTableViewRowHeaderSupport) hitTest:]
messes things up. Workaround: get the row and the column, get the cell view and call hitTest
on the cell view.
override func mouseDown(with event: NSEvent) {
let localPoint = self.convert(event.locationInWindow, from: nil)
let column = self.column(at: localPoint)
let row = self.row(at: localPoint)
if column >= 0 && row >= 0 {
if let cellView = self.view(atColumn: column, row: row, makeIfNecessary: false) {
if let cellSuperViewPoint = cellView.superview?.convert(localPoint, from: self) {
if let view = cellView.hitTest(cellSuperViewPoint) {
Swift.print("\(view.className)")
}
}
}
}
}