I have specific requirements because I have a (child) NSTableView embedded in (parent) NSTableCellView, some data needs to be shown inside the child's NSTableView.
The data can be displayed and edited without issues. But when I edit and scroll down without pressing "return" or clicking anywhere, the NSTextField in the child NSTableView will end editing status, trigger @IBAction, and can no longer continue editing after I scroll up.
I've made a simple project for testing below:
//
// ViewController.swift
// test
//
// Created by Tilseam on 2023/2/20.
//
import Cocoa
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate{
@IBOutlet weak var parentTableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
parentTableView.delegate = self
parentTableView.dataSource = self
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
var childViewData = ["111","222","333","444","555","666","777","888","999"]
extension ViewController{
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "parentTableView"){
return 200
}
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "childTableView"){
return 30
}
return 0
}
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
return NSTableRowView()
}
func numberOfRows(in tableView: NSTableView) -> Int {
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "parentTableView"){
return 8
}
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "childTableView"){
return 1
}
return 0
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "parentTableView"),
let parentCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "parentCellView"), owner: self) as? ParentCellView,
let childTableView = parentCellView.childTableView as? ChildTableView{
parentCellView.textField?.stringValue = "\(row)"
childTableView.childTableData = childViewData[row]
childTableView.delegate = self
childTableView.dataSource = self
return parentCellView
}
if tableView.identifier == NSUserInterfaceItemIdentifier(rawValue: "childTableView"),
let tableView = tableView as? ChildTableView,
let childCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "childCellView"), owner: self) as? NSTableCellView{
childCellView.textField?.stringValue = tableView.childTableData ?? ""
return childCellView
}
return nil
}
}
class ParentCellView: NSTableCellView{
@IBOutlet weak var childTableView: NSTableView!
}
class ChildTableView: NSTableView{
var childTableData: String?
}
Code: https://github.com/tilseam/test
As is shown in the gif, when I write in the NSTextfield embedded in the Parent's NSTableView, scroll down and scroll back, the first responder is still the NSTextfield, but in the second situation, I write in the NSTextfield embedded in the Child's NSTableView, the TextField will end editing after the cellView gets out of the visible area.
What I've tried:
I inferred that the reason for the problem is the reuse of the cellView, when I scroll down, the NSTableCellView of the parent's NSTableView gets reused, and ignores the currentEditor() in the subviews. However, I cannot find any solution to prevent the NSTextfield from resigning. I've tried to override the textFieldShouldEndEditing(_:) to return false, or override the prepareForReuse() , but found no good solution.
Perhaps it would be best to save editing progress and restore it upon returning to the text field. However, I have not yet found out how to make the text field become the first responder again and restore the editing state."