跟踪 NSCell 的位置变化

Tracking the position of a NSCell on change

我有一个 NSTableView 并且想在用户滚动 tableView 时跟踪其包含的 NSCell 的位置。

我找不到任何有用的东西。如果有人能引导我走向正确的方向,那就太好了!

编辑:

感谢 @Ken Thomases@Code Different,我才意识到我正在使用基于视图的 tableView,使用 tableView(_ tableView:viewFor tableColumn:row:),其中 returns 一个 NSView。

但是,那个 NSView 本质上是一个 NSCell。

let cell = myTableView.make(withIdentifier: "customCell", owner: self) as! MyCustomTableCellView // NSTableCellView

所以我真的希望我最初的问题没有误导。我仍在寻找一种方法 如何跟踪个人的位置 cells/views

我在 IB 中将 NSScrollView(包含 tableView)的行为设置为 Copy on Scroll

但是当我检查 view/cells frame(在我的 MyCustomTableCellView 子类的 viewWillDraw 中)的 x 和 y 时,它仍然是 0, 0

根据编辑后的问题更新:

首先,请注意,NSTableCellView 不是 NSCell 也不是它的子类。当您使用基于视图的 table 时,您没有对单元格视图使用 NSCell

此外,视图的 frame 始终与其直接父视图的边界相关。这不是一个绝对的立场。而且 cell view 的 superview 不是 table view 也不是 scroll view。单元格视图位于行视图内。这就是为什么您的单元格视图的原点位于 0, 0。

您可以使用 NSTableViewframeOfCell(atColumn:row:) 来确定给定单元格视图在 table 视图中的位置。不过,我仍然认为这不是一个好方法。请参阅下面我的原始答案的最后一段:


原回答:

Table 视图并不像您想象的那样 "contain" 一堆 NSCell。此外,NSCells 没有位置。基于 NSCell 的复合视图的全部意义在于它们比为每个单元格使用单独对象的架构轻得多。

通常,每个 table 列有一个 NSCell。当 table 视图需要在一列中绘制单元格时,它会使用一个单元格的数据配置该列的 NSCell 并告诉它在该单元格的位置绘制。然后,它将相同的 NSCell 配置为下一个单元格的数据,并告诉它在下一个位置绘制。等等

要执行您想要的操作,您可以将滚动视图配置为不在滚动时复制。然后,table 视图将被要求在滚动时绘制所有内容。然后,您将实现 tableView(_:willDisplayCell:for:row:) 委托方法并将 alpha 值应用于滚动视图顶部和底部边缘的单元格。

但这可能不是一个好方法。

我认为您可以通过向部分透明的滚动视图添加浮动子视图来获得更好的运气,并在背景颜色中从完全不透明到完全透明进行渐变。因此,不是单元格淡出并让背景显示出来,而是将另一个视图放在顶部,只让部分单元格显示出来。

NSScrollView 不使用委托。它使用通知中心通知观察者发生了变化。下面的解决方案假设垂直滚动。

override func viewDidLoad() {
    super.viewDidLoad()

    // Observe the notification that the scroll view sends out whenever it finishes a scroll
    let notificationName = NSNotification.Name.NSScrollViewDidLiveScroll
    NotificationCenter.default.addObserver(self, selector: #selector(scrollViewDidScroll(_:)), name: notificationName, object: scrollView)

    // Post an intial notification to so the user doesn't have to start scrolling to see the effect
    scrollViewDidScroll(Notification(name: notificationName, object: scrollView, userInfo: nil))
}

// Whenever the scroll view finished scrolling, we will start coloring the rows
// based on how much they are visible in the scroll view. The idea is we will
// perform hit testing every n-pixel in the scroll view to see what table row
// lies there and change its color accordingly
func scrollViewDidScroll(_ notification: Notification) {
    // The data's part of a table view begins with at the bottom of the table's header
    let topEdge = tableView.headerView!.frame.height
    let bottomEdge = scrollView.bounds.height

    // We are going to do hit-testing every 10 pixel. For best efficiency, set
    // the value to your typical row's height
    let step = CGFloat(10.0)

    for y in stride(from: topEdge, to: bottomEdge, by: step) { 
        let point = NSPoint(x: 10, y: y)                        // the point, in the coordinates of the scrollView
        let hitPoint = scrollView.convert(point, to: tableView) // the same point, in the coordinates of the tableView

        // The row that lies that the hitPoint
        let row = tableView.row(at: hitPoint)

        // If there is a row there
        if row > -1 {
            let rect = tableView.rect(ofRow: row)                     // the rect that contains row's view
            let rowRect = tableView.convert(rect, to: scrollView)     // the same rect, in the scrollView's coordinates system
            let visibleRect = rowRect.intersection(scrollView.bounds) // the part of the row that visible from the scrollView
            let visibility = visibleRect.height / rowRect.height      // the percentage of the row that is visible

            for column in 0..<tableView.numberOfColumns {
                // Now iterate through every column in the row to change their color
                if let cellView = tableView.view(atColumn: column, row: row, makeIfNecessary: true) as? NSTableCellView {
                    let color = cellView.textField?.textColor

                    // The rows in a typical text-only tableView is 17px tall
                    // It's hard to spot their grayness so we exaggerate the
                    // alpha component a bit here:
                    let alpha = visibility == 1 ? 1 : visibility / 3

                    cellView.textField?.textColor = color?.withAlphaComponent(alpha)
                }
            }
        }
    }
}

结果:

我刚刚自己解决了这个问题。

只需将内容视图 postsBoundsChangedNotifications 设置为 true 并向 NSViewBoundsDidChange 的 NotificationCenter 添加观察者。很有魅力!