NSTableView reloadData (forRowIndexes: columnIndexes :) прерывает автоматическое расположение - PullRequest
0 голосов
/ 25 июня 2018

У меня есть NSTableView, который может менять местами представления разных ячеек на основе значений данных для строки. Когда модель изменится, я перезагружу таблицу, и делегат таблицы предоставит правильное представление ячейки таблицы для новых данных.

В таблице используется автоматическое расположение для представлений ячеек Все виды ячеек изначально загружаются нормально. Обновляя таблицу после смены модели, я получаю разные результаты в зависимости от того, звоню я reloadData() или reloadData(forRowIndexes:columnIndexes). При использовании reloadData() представление ячейки загружается, и автоматическое расположение работает нормально. Если я использую reloadData(forRowIndexes:columnIndexes), autolayout дает совершенно другие, неожиданные результаты.

Я создал пример проекта, чтобы продемонстрировать проблему. Здесь - изображение настройки проекта, включая ограничения, установленные для представлений ячейки таблицы. Существует два шаблона строк, один с синим представлением (четные строки), другой с зеленым (нечетные строки), которые должны охватывать ширину таблицы (без небольшого отступа). Контроллер предоставляет вид ячеек:

class TableController: NSObject {
    @IBOutlet weak var tableView: NSTableView!
    var colorData = [1, 0, 1, 0]

    @IBAction func swapLine(_ sender: Any) {
        colorData[1] = (colorData[1] + 1) % 2
    //        tableView.reloadData()
        tableView.reloadData(forRowIndexes: [1], columnIndexes: [0])
    }
}

extension TableController: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return colorData.count
    }
}

extension TableController: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let cellId = (colorData[row]) % 2 == 0 ? "EvenCell" : "OddCell"
        return tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(cellId), owner: self)
    }
}

Кнопка в интерфейсе просто меняет данные для строки 1 и перезагружает данные. Первоначальный вид выглядит как это (чередуются зеленые и синие линии). Если вы используете reloadData(), это выглядит как this (строка 1 изменена с синего на зеленый). Но если вы используете reloadData(withRowIndexes:columnIndexes:), вид ячеек сжимается до 40 точек шириной, а не 480, как в других. Вот отладчик представления, показывающий представление ячейки с неправильным размером и показывающий неоднозначные ограничения ширины (этого не происходит при использовании reloadData()).

В документации упоминается, что представление строки повторно используется с reloadData(forRowIndexes:columnIndexes:), но не с reloadData(), что я проверял. Я полагаю, что повторное использование представления строк вызывает проблемы с автопоставкой, но я не могу найти никакой связи. Ничего не нашлось в SO, заметках о выпуске AppKit, видео WWDC, поиске в Google или в том, что я постучал по столу. Был бы действительно благодарен за помощь.

Обновление: Вот код для ColorView:

class ColorView: NSView {
    @IBInspectable var intrinsicHeight: CGFloat = 20
    @IBInspectable var color: NSColor = NSColor.blue

    override var intrinsicContentSize: NSSize {
        return NSSize(width: NSView.noIntrinsicMetric, height: intrinsicHeight)
    }

    override func draw(_ dirtyRect: NSRect) {
        color.setFill()
        dirtyRect.fill()
    }
}

Ответы [ 2 ]

0 голосов
/ 18 января 2019

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

class CustomRowView: NSTableRowView {
  override func addSubview(_ view: NSView) {
    super.addSubview(view)

    // Add constraints NSTableView is supposed to set up
    view.topAnchor.constraint(equalTo: topAnchor).isActive = true
    view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    view.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0).isActive = true
    view.layoutSubtreeIfNeeded()
  }
}
0 голосов
/ 15 августа 2018

Я думаю, у меня это работает. Если я вызову layoutSubtreeIfNeeded() для ячейки непосредственно перед ее возвратом (так что все ее подпредставления, такие как динамический текст, уже заданы), то, похоже, это сработает.

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  //...

  cell.layoutSubtreeIfNeeded()
  return cell
}

Надеюсь, это поможет.

...