Визуальный сбой с NSTableView с использованием чередующихся цветов строк в macOS Big Sur

Я использую NSTableView с usesAlternatingRowBackgroundColors, установленным на true.

Как только я

  1. Добавьте много столбцов, например, 15 и
  2. Установите для параметра «Высота ячеек» значение ›0

в таблице виден визуальный сбой, при котором чередующиеся строки неравномерно распределены по высоте:

введите описание изображения здесь

Это происходит только в macOS Big Sur. macOS Mojave и macOS Catalina работают нормально. Я экспериментировал практически с любой комбинацией настроек и стилей, используя последнюю версию Xcode 12.4.

Мой ViewController довольно прост:

class ViewController: NSViewController {

    @IBOutlet weak var tableView: NSTableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        for columnIndex in 0..<15 {
            let tableColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "\(columnIndex)"))
            tableColumn.title = "CustomColumn \(columnIndex)"
            tableColumn.width = 150
            tableView.addTableColumn(tableColumn)
        }
    }
}

И конфигурация NSTableView в Интерфейсном Разработчике также довольно утомительна, за исключением отрегулированной высоты расстояния между ячейками:

введите описание изображения здесь

Было бы здорово, если бы кто-нибудь мог подтвердить проблему и, возможно, поделиться с ней обходным путем.

Вы можете найти демонстрационный проект на странице https://github.com/fheidenreich/table-test.


person fhe    schedule 03.02.2021    source источник
comment
У меня нет ответа, но я могу подтвердить, что вы не единственный, кто это видит. В приложении с 26 столбцами глюки проявляются и у меня. Я сам прибег к рисованию фона.   -  person Bryan    schedule 27.05.2021


Ответы (2)


Я тестировал образец проекта на MacOS 11.0 и MacOS 11.2.1, но не смог воспроизвести сбой. Но это не неожиданно, поскольку сам код выглядит нормально.

Возможно, проблема связана с промежуточной версией MacOS или определенной комбинацией аппаратного программного обеспечения. Если проблема не исчезнет, ​​можно попробовать нарисовать фон вручную, создав подкласс NSTableView.

class TableViewEx: NSTableView {
    
    public override func drawBackground(inClipRect clipRect: NSRect) {
        
        guard usesAlternatingRowBackgroundColors else {
            super.drawBackground(inClipRect: clipRect)
            return
        }
        
        backgroundColor.setFill()
        clipRect.fill()
        let effectiveRowHeight = rowHeight + self.intercellSpacing.height
        
        // This color requires 10.14+. There is an older, deprecated property on NSColor to get this value if needed.
        let altColor = NSColor.alternatingContentBackgroundColors.last ?? backgroundColor
        altColor.setFill()
        
        let rowIndex = Int((clipRect.minY / effectiveRowHeight).rounded(.down))
        
        for i in rowIndex..<Int.max {
            if i % 2 == 0 { continue }
            let rect = NSRect(
                x: clipRect.minX,
                y: CGFloat(i) * effectiveRowHeight,
                width: clipRect.width,
                height: effectiveRowHeight
            )
            rect.fill()
            if rect.maxY >= clipRect.maxY { break }
        }
    }
}

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

person sdochow    schedule 16.02.2021

Для тех, кто решил рисовать в ClipRect самостоятельно, вот лучший подход для этого. У него есть несколько преимуществ:

  1. Он рисует строки только в той части tableView, где фактические NSRowView экземпляры не отображаются, поэтому это намного эффективнее.

  2. Когда вы прокручиваете край tableView (так что края начинают переходить в резинку), современные стили NSTableView ничего не рисуют в области резиновых полос. Этот подход следует этому соглашению. Без этого ФАКТИЧЕСКИЕ строки таблицы перестанут рисоваться по мере того, как вы перетаскиваете их за край, но ФАКТИЧЕСКИЕ строки таблицы, которые мы рисуем, не будут рисоваться, что будет выглядеть странно.

override func drawBackground(inClipRect clipRect: NSRect)
{
    backgroundColor.setFill()
    clipRect.fill()
        
    let effectiveRowHeight: CGFloat = rowHeight + self.intercellSpacing.height
        
    let altColor: NSColor = NSColor.alternatingContentBackgroundColors.last ?? backgroundColor
    altColor.setFill()
    
    // Don't draw rows that the tableView already has
    let rowIndex = max(Int((clipRect.minY / effectiveRowHeight).rounded(.down)), numberOfRows)
        
    // How many rows do we need to draw?
    // Don't draw rows in the "rubber banding" zone if the user scrolls down past the end of the table, or horizontally off the side.
    // That matches how modern NSTableView draws NSRowView instances, so the table will look uniform during "rubber-banding"
    let maxRows = Int((self.bounds.size.height/effectiveRowHeight).rounded(.up))
    let tableOrigin: CGFloat = bounds.origin.x
    let tableWidth: CGFloat = bounds.size.width
        
    guard rowIndex < maxRows else {
        return
    }
        
    for i in rowIndex ..< maxRows
    {
        if i % 2 != 0
        {
            let rect = NSRect(x: tableOrigin, y: (CGFloat(i) * effectiveRowHeight), width: tableWidth, height: effectiveRowHeight)
            rect.fill()
        }
    }
}
person Bryan    schedule 27.05.2021