NSTableView.setNeedsDisplay()不会仅在附加的格式化程序上重绘

时间:2019-04-22 14:10:05

标签: swift nstableview macos-mojave

我正在使用基于NSTableView的视图,该视图带有显示日期的列,并且表格单元格视图使用共享的DateFormatter。

let view: NSTableCellView? = tableView.makeView(withIdentifier: column.identifier, owner: self) as! NSTableCellView?
let entry = (logController.arrangedObjects as! [LogEntry])[row]
    switch column.identifier {
    case columnDateKey:
        view?.textField?.formatter = sharedDateFormatter
        view?.textField?.objectValue = entry.date

应用程序具有用户首选项,可以选择日期格式和以前的代码

tableView.setNeedsDisplay(tableView.rect(ofColumn: tableView.column(withIdentifier: columnDateKey)))

将使用新的日期格式刷新该列。

使用macOS Mojave不会发生这种情况。调查显示,尽管为底层TableView调用了drawRect:,但没有对tableView(:viewFor:row :)进行任何调用来获取表单元格视图的新值。调用tableView.reloadData(forRowIndexes:columnIndexes :)确实会导致对tableView(:viewFor:row :)的调用,但是显示不会刷新(尽管对于tableView.reloadData()也会如此)。

重绘的任何外部原因,例如选择一行可以正确更新该区域。我看到的另一件事是,如果长表缓慢滚动,最终将导致出现新格式,尽管现有单元格在滚动回到滚动很长一段距离才返回之前不会改变。这似乎可以推断出,仅当附加的格式化程序的配置发生更改时(尽管内容的值发生了更改),才认为有些缓存视图没有发生更改。

随着Mojave的引入,这种行为发生了变化,我发现很难相信没有其他人进行过举报,因此开始质疑我的原始代码。我想念什么吗?

以下测试代码演示了该问题,对于setNeedsDisplay的变体,未打印“查看请求”消息,并且仅针对reloadData()重绘了显示内容

styleButton是用于切换数字格式的复选框,而refreshButton是用于请求重绘的操作按钮

将值设置为随机值将导致预期的重绘行为

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet weak var window: NSWindow!
    @IBOutlet weak var table: NSTableView!
    @IBOutlet weak var styleButton: NSButton!
    @IBOutlet weak var refreshButton: NSButton!
    @IBOutlet weak var testView: NSView!

    let numberFormatter = NumberFormatter()

    func applicationWillFinishLaunching(_ notification: Notification) {
    numberFormatter.numberStyle = symbolButton.state == NSControl.StateValue.on ? NumberFormatter.Style.decimal : NumberFormatter.Style.none
    }

    @IBAction func refresh(sender: Any?) {
        numberFormatter.numberStyle = styleButton.state == NSControl.StateValue.on ? NumberFormatter.Style.decimal : NumberFormatter.Style.none
        table.setNeedsDisplay(table.rect(ofColumn: 0))
        // table.needsDisplay = true
        // table.reloadData(forRowIndexes: IndexSet(integersIn: 0..<table.numberOfRows), columnIndexes:[0])
        // table.reloadData()
    }
}

extension AppDelegate: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        if tableView == table {
            return 40
        }
        return 0
    }
}

extension AppDelegate: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        print("View requested")
        guard tableColumn != nil else {
            return nil
        }
        let column = tableColumn!
        if tableView == table {
            let view: NSTableCellView? = tableView.makeView(withIdentifier: column.identifier, owner: self) as! NSTableCellView?
            view?.textField?.formatter = numberFormatter
            view?.textField?.objectValue = 123.456
            return view
        }
        return nil
    }
}

1 个答案:

答案 0 :(得分:0)

错误地依赖view.setNeedsDisplay来自动更新子视图。事实并非如此(尽管以前似乎是这样工作的)-请参阅上面Willeke的评论