【问题标题】:How to implement NSTableViewRowlView like Xcode IB object selector如何像 Xcode IB 对象选择器一样实现 NSTableViewRowlView
【发布时间】:2018-07-29 23:37:15
【问题描述】:

我正在尝试实现一个类似于 Xcode IB 对象选择器(右下面板)的NSTableView。 As shown below when a row is selected a full width horizo​​ntal 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
                    }
                }
            }
        }
    }

然后在tableViewSelectionDidChange(:)方法中简单地设置变量lastSelectedRow = tableView.selectedRow的值。

我认为可能需要对 tableView 进行子类化,以确保在同一更新周期中重绘两行。

【问题讨论】:

    标签: nstablerowview


    【解决方案1】:

    这个 NSTableRowView 子类似乎工作正常,在重绘上面的行时没有明显的滞后。

    解决方案是正确覆盖isSelected,并每次在上面的行上设置needsDisplay

    /// 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 this and whenever it is changed set the previous row to be updated
        override var isSelected: Bool {
            didSet {
                if let tableView = self.superview as? NSTableView {
                    let row = tableView.row(for: self)
                    if row > 0 {
                        tableView.rowView(atRow: row-1, makeIfNecessary: false)?.needsDisplay = true
                    }
                }
            }
        }
    
    
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
        }
    
        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()
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-02
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2013-09-10
      • 2021-07-09
      相关资源
      最近更新 更多