I want to programmatically scroll an NSTableView so that a particular row is centered. It's simple to scroll the NSTableView so that a particular row is visible:
[theTableView scrollRowToVisible:pos];
However, usually the row in question is at the bottom of the visible area, whereas I'd like it to be roughly in the center.
Another stupid approach is to scroll a few rows beyond the one I want to be visible, e.g., something like:
// pos = index of desired row
// numRows = number of rows in the table
NSRect visibleRect = [resultsTableView visibleRect];
NSRange visibleRange = [resultsTableView rowsInRect:visibleRect];
NSUInteger offset = visibleRange.length/2;
NSUInteger i;
if (pos + offset >= numRows)
i = numRows - 1;
else if (pos < visibleRange.length)
i = pos;
else
i = pos + offset;
[resultsTableView scrollRowToVisible:i];
This works if all rows are exactly the same height, but I am interested in making rows with different heights.
Is there a better, perhaps more direct, way to do this? For example, I have noticed that the NSTableView is wrapped in an NSScrollView.... (The table was made using Interface Builder.)
Thanks!
This works for me on a sub-class of your NSTableView
func scrollRowToVisible(row: Int, animated: Bool) {
if animated {
guard let clipView = superview as? NSClipView,
let scrollView = clipView.superview as? NSScrollView else {
assertionFailure("Unexpected NSTableView view hiearchy")
return
}
let rowRect = rect(ofRow: row)
var scrollOrigin = rowRect.origin
let tableHalfHeight = clipView.frame.height * 0.5
let rowRectHalfHeight = rowRect.height * 0.5
scrollOrigin.y = (scrollOrigin.y - tableHalfHeight) + rowRectHalfHeight
if scrollView.responds(to: #selector(NSScrollView.flashScrollers)) {
scrollView.flashScrollers()
}
clipView.animator().setBoundsOrigin(scrollOrigin)
} else {
scrollRowToVisible(row)
}
}
then you can call:
self.tableView.scrollRowToVisible(self.tableView.selectedRow, animated:true)
after you've selected the row
I stumbled on this question while looking for a solution in Swift 4 (thanks to @AntoniodePerio for his answer that I ported):
extension NSTableView {
func centreRow(row: Int, animated: Bool) {
self.selectRowIndexes(IndexSet.init(integer: row), byExtendingSelection: false)
let rowRect = self.frameOfCell(atColumn: 0, row: row)
if let scrollView = self.enclosingScrollView {
let centredPoint = NSMakePoint(0.0, rowRect.origin.y + (rowRect.size.height / 2) - ((scrollView.frame.size.height) / 2))
if animated {
scrollView.contentView.animator().setBoundsOrigin(centredPoint)
} else {
self.scroll(centredPoint)
}
}
}
}
call with:
@IBOutlet weak var myTable: NSTableView!
myTable.centreRow(row: 10, animated: true)
I would take a look at NSView
's -scrollPoint:
and -scrollRectToVisible:
methods. Try doing something like:
if (tableView.mainScrollView) {
CGFloat yLocation = row.frame.origin.y + tableView.mainScrollView.contentView.frame.size.height / 2;
if (yLocation > tableView.frame.size.height) yLocation = tableView.frame.size.height;
NSPoint rowPoint = NSMakePoint(0, row.frame.origin.y + );
[tableView scrollPoint:rowPoint];
}
I'm not sure if this works, since i can't test it, let me know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With