跟踪 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。
您可以使用 NSTableView
的 frameOfCell(atColumn:row:)
来确定给定单元格视图在 table 视图中的位置。不过,我仍然认为这不是一个好方法。请参阅下面我的原始答案的最后一段:
原回答:
Table 视图并不像您想象的那样 "contain" 一堆 NSCell
。此外,NSCell
s 没有位置。基于 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 添加观察者。很有魅力!
我有一个 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。
您可以使用 NSTableView
的 frameOfCell(atColumn:row:)
来确定给定单元格视图在 table 视图中的位置。不过,我仍然认为这不是一个好方法。请参阅下面我的原始答案的最后一段:
原回答:
Table 视图并不像您想象的那样 "contain" 一堆 NSCell
。此外,NSCell
s 没有位置。基于 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 添加观察者。很有魅力!