我正在尝试实现一个NSTableView
看起来类似于 Xcode IB 对象选择器(右下面板)的。As shown below when a row is selected a full width horizontal line is draw above and below the selected row.
我已经成功创建了一个子类,NSTableRowView
并使用了 isNextRowSelected 属性来确定是否绘制全宽分隔符,这几乎可以工作。
问题是所选行上方的行没有被重绘,除非您碰巧选择了一行,然后立即选择了它下方的行。
如何有效地让 NSTableView 每次都重绘所选行上方的行?
/// This subclass draws a partial line as the separator for unselected rows and a full width line above and below for selected rows
/// | ROW |
/// | ---------- | unselected separator
/// |------------| selected separator on row above selected row
/// | ROW |
/// |------------| selected separator
///
/// Issue: Row above selected row does not get redrawn when selected row is deselected
class OSTableRowView: NSTableRowView {
let separatorColor = NSColor(calibratedWhite: 0.35, alpha: 1)
let selectedSeparatorColor = NSColor(calibratedWhite: 0.15, alpha: 1)
let selectedFillColor = NSColor(calibratedWhite: 0.82, alpha: 1)
override func drawSeparator(in dirtyRect: NSRect) {
let yBottom = self.bounds.height
let gap: CGFloat = 4.0
let xLeft: CGFloat = 0.0
let xRight = xLeft + self.bounds.width
let lines = NSBezierPath()
/// Draw a full width separator if the item is selected or if the next row is selected
if self.isSelected || self.isNextRowSelected {
selectedSeparatorColor.setStroke()
lines.move(to: NSPoint(x: xLeft, y: yBottom))
lines.line(to: NSPoint(x: xRight, y: yBottom))
lines.lineWidth = 1.0
} else {
separatorColor.setStroke()
lines.move(to: NSPoint(x: xLeft+gap, y: yBottom))
lines.line(to: NSPoint(x: xRight-gap, y: yBottom))
lines.lineWidth = 0.0
}
lines.stroke()
}
override func drawSelection(in dirtyRect: NSRect) {
if self.selectionHighlightStyle != .none {
let selectionRect = self.bounds
selectedSeparatorColor.setStroke()
selectedFillColor.setFill()
selectionRect.fill()
}
}
}
在阅读了其他几篇文章后,我尝试添加代码以重绘前一行。这似乎没有效果。
func selectionShouldChange(in tableView: NSTableView) -> Bool {
let selection = tableView.selectedRow
if selection > 0 {
tableView.setNeedsDisplay(tableView.rect(ofRow: selection-1))
tableView.displayIfNeeded()
}
return true
}
这也不是。
func tableViewSelectionDidChange(_ notification: Notification) {
guard let tableView = self.sidebarOutlineView else {
return
}
let row = tableView.selectedRow
if row > 0 {
tableView.setNeedsDisplay(tableView.rect(ofRow: row-1))
print("row-1 update rect: \(tableView.rect(ofRow: row-1))")
}
}
似乎很奇怪,这些都没有触发重绘行 - 我在这里遗漏了什么!
编辑:好的,我发现一些似乎可以正常工作的东西 - 在 XCode tableView 中不存在的取消选择的行上方的行的重绘中仍然存在明显的滞后。
var lastSelectedRow = -1 {
didSet {
guard let tableView = self.sidebarOutlineView else {
return
}
if oldValue != lastSelectedRow {
if oldValue > 0 {
if let view = tableView.rowView(atRow: oldValue-1, makeIfNecessary: false) {
view.needsDisplay = true
}
}
if lastSelectedRow > 0 {
if let view = tableView.rowView(atRow: lastSelectedRow-1, makeIfNecessary: false) {
view.needsDisplay = true
}
}
}
}
}
然后简单地lastSelectedRow = tableView.selectedRow
在tableViewSelectionDidChange(:)
方法中设置变量的值。
我认为可能需要对 tableView 进行子类化,以确保在同一更新周期中重绘两行。