首页 > 解决方案 > Cocoa:在 Swift 4 的 NSTableView 中添加文本单元格(NSTextField)后得到通知并开始编辑文本单元格?

问题描述

我在这里使用 TableView 做了一个简单的演示:https ://github.com/deadcoder0904/TableViewDemo

我已将Defaults模块用作依赖项

我的项目看起来像

追随你的梦想

所有代码ViewController.swift如下 -

import Cocoa
import Defaults

extension Defaults.Keys {
    static let dreams = Defaults.Key<Array<String>>("dreams", default: [
        "Hit the gym",
        "Run daily",
        "Become a millionaire",
        "Become a better programmer",
        "Achieve your dreams"
        ])
}

class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {

    @IBOutlet weak var table: NSTableView!
    var dreams = defaults[.dreams]
    var selectedRow:Int = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        table.dataSource = self
        table.delegate = self
    }

    override var acceptsFirstResponder : Bool {
        return true
    }

    override func keyDown(with theEvent: NSEvent) {
        if theEvent.keyCode == 51 {
            removeDream()
        }
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
        let table = notification.object as! NSTableView
        selectedRow = table.selectedRow
    }

    func numberOfRows(in tableView: NSTableView) -> Int {
        return dreams.count
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let dream = table.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
        dream.textField?.stringValue = dreams[row]

        return dream
    }

    @IBAction func addTableRow(_ sender: Any) {
        addNewDream()
    }

    @IBAction func removeTableRow(_ sender: Any) {
        removeDream()
    }

    func addNewDream() {
        dreams.append("Double Click or Press Enter to Add Item")
        table.beginUpdates()
        let last = dreams.count - 1
        table.insertRows(at: IndexSet(integer: last), withAnimation: .effectFade)
        table.scrollRowToVisible(last)
        table.selectRowIndexes([last], byExtendingSelection: false)
        table.endUpdates()
        saveDreams()
    }

    func removeDream() {
        if selectedRow >= dreams.count {
            selectedRow = dreams.count - 1
        }
        if selectedRow != -1 {
            dreams.remove(at: selectedRow)
            table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
        }
        saveDreams()
    }

    func saveDreams() {
        defaults[.dreams] = dreams
    }
}

我想做两件事-

  1. 在编辑文本单元格后收到通知,以便我可以使用默认模块保存更改的数据

  2. 通过单击加号添加新数据后,它会添加双击或按 Enter 以添加项目,但我想要添加空字符串,我可以使用 "" 但我也希望它能够集中并可编辑所以用户无需双击或按 Enter即可开始在其中输入文本。

我还想要 Swift 4 而不是 Objective-C 的解决方案。如何做到这一点?

标签: swiftxcodemacoscocoaswift4

解决方案


使用 Cocoa Bindings,它非常强大并且节省了大量的样板代码。

简短教程:

编辑:要充分利用 KVC,数据源必须是具有属性的NSObject子类dynamic

  • 创建一个简单的类Dream(该description属性是可选的)

    class Dream : NSObject {
        @objc dynamic var name : String
        init(name : String) { self.name = name }
        override var description : String { return "Dream " + name }
    }
    
  • 在视图控制器中声明数据源数组

    var dreams = [Dream]()
    
  • 并替换var selectedRow:Int = 0

    @objc dynamic var selectedIndexes = IndexSet()
    
  • 转到界面生成器

    • 选择表格视图,按下⌥⌘7以转到绑定检查器。
      绑定Selection IndexesView Controller 模型键路径 selectedIndexes
      按下⌥⌘6并将dataSource(通过拖放)连接到视图控制器 ( 在此处输入图像描述) 。

    • 选择表格列中File 1的文本字段。Table Cell View最简单的方法是⌃⇧click在文本字段区域。
      按下⌥⌘7并绑定ValueTable Cell View 模型键路径 objectValue.name( ! )

  • 在视图控制器中填充数据源数组viewDidLoad(我不知道该框架,所以我将其省略)并重新加载表视图。

    override func viewDidLoad() {
        super.viewDidLoad()
        let dreamNames = ["Hit the gym", "Run daily", "Become a millionaire", "Become a better programmer", "Achieve your dreams"]
        dreams = dreamNames.map{Dream(name: $0)}
        table.reloadData()         
    }
    
  • 删除acceptsFirstResponder

  • 删除tableViewSelectionDidChange
  • 删除tableView:viewFor:row:
  • 添加

    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return dreams[row]
    }
    
  • 替换addNewDream

    func addNewDream() {
         let last = dreams.count
         dreams.append(Dream(name: "Double Click or Press Enter to Add Item"))
         table.insertRows(at: IndexSet(integer: last), withAnimation: .effectGap)
         table.scrollRowToVisible(last)
         table.selectRowIndexes([last], byExtendingSelection: false)
    
         saveDreams()
     }
    
  • 替换removeDream()

    func removeDream() {
         guard let selectedRow = selectedIndexes.first else { return }
         dreams.remove(at: selectedRow)
         table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
    
         saveDreams()
     }
    

要在之后编辑文本时保存数组,您必须实现委托方法controlTextDidEndEditing(_:)

override func controlTextDidEndEditing(_ obj: Notification) {
    saveDreams()
}

并在 Interface Builderdelegate中将表格视图中的文本字段连接到视图控制器。


推荐阅读