Отслеживание позиции NSCell при изменении

У меня есть NSTableView и хотите отследить положение его содержимого NSCells, когда tableView прокручивается пользователем.

Я не мог найти ничего полезного. Было бы здорово, если бы кто-то мог привести меня в правильном направлении!

РЕДАКТИРОВАТЬ:

Благодаря @Ken Thomases и @Code Different я только что понял, что использую tableView на основе представления, используя tableView(_ tableView:viewFor tableColumn:row:), который возвращает NSView.

Однако этот NSView по сути является NSCell.

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

Поэтому я очень надеюсь, что мой первоначальный вопрос не вводил в заблуждение. Я все еще ищу способ, как отследить положение отдельных ячеек / представлений.

Я установил поведение NSScrollView (который содержит tableView) для Copy on Scroll в IB.

Но когда я проверяю x и y вида / ячеек frameviewWillDraw моего подкласса MyCustomTableCellView) он остается 0, 0,

3 ответа

Решение

Я просто решил проблему самостоятельно.

Просто установите вид содержимого postsBoundsChangedNotifications в true и добавил наблюдателя в NotificationCenter для NSViewBoundsDidChange, Работает как шарм!

Обновление на основе отредактированного вопроса:

Во-первых, просто чтобы вы знали, NSTableCellView это не NSCell ни подкласс этого. Когда вы используете таблицу на основе представления, вы не используете NSCell для просмотра ячеек.

Кроме того, вид frame всегда относительно границ своего непосредственного супервизора. Это не абсолютная позиция. И суперпредставление представления ячейки не является представлением таблицы или представлением прокрутки. Виды ячеек находятся внутри видов строк. Вот почему ваш вид ячейки находится в 0, 0.

Вы могли бы использовать NSTableView"s frameOfCell(atColumn:row:) чтобы определить, где данное представление ячейки находится в представлении таблицы. Я все еще не думаю, что это хороший подход. Пожалуйста, смотрите последний абзац моего оригинального ответа ниже:


Оригинальный ответ:

Табличные представления не "содержат" связку NSCellкак вы думаете. Также, NSCellу них нет позиции. Весь смысл NSCellсоставные представления основаны на том, что они намного легче, чем архитектура, в которой для каждой ячейки используется отдельный объект.

Обычно есть один NSCell для каждого столбца таблицы. Когда табличное представление должно нарисовать ячейки в столбце, оно настраивает NSCell с данными для одной ячейки и говорит ему рисовать в позиции этой ячейки. Затем он настраивает тот же NSCell с данными для следующей ячейки и говорит ей рисовать на следующей позиции. И т.п.

Чтобы сделать то, что вы хотите, вы можете настроить представление прокрутки, чтобы не копировать при прокрутке. Затем в табличном представлении будет предложено рисовать все, когда оно прокручивается. Затем вы бы реализовать tableView(_:willDisplayCell:for:row:) метод делегата и применение альфа-значения к ячейкам в верхнем и нижнем краях представления прокрутки.

Но это, вероятно, не очень хороший подход.

Я думаю, что вам может повезти, добавив плавающие подпредставления в представление прокрутки, которые являются частично прозрачными, с градиентом от полностью непрозрачного до полностью прозрачного в цвете фона. Таким образом, вместо того, чтобы ячейки исчезали и пропускали фон, вы помещаете другой вид сверху, который пропускает только часть ячеек.

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)
                }
            }
        }
    }
}

Результат:

Другие вопросы по тегам