(Macos cocoa) How to preserve an NSTextField editing state?

66 views Asked by At

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.

enter image description here

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

enter image description here

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:

  1. 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.

  2. 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."

0

There are 0 answers