swift - 如何正确地向 NSTableView 添加一行

标签 swift macos nstableview

首先我必须指出,这是我在 Swift 中的第一个 GUI 应用程序 (XIB),换句话说,我正在努力学习 Swift 和 MacOS 软件开发。我查看了 Stack 上的几个问题,以及关于 NSTableView 的 Apple 文档,但我被卡住了。

尝试制作一个简单的应用程序来读取所选文件的某些属性。我有一个自定义 NSView用户拖放文件并从中读取一些属性 - 没问题。

>>> print("\(fileDataToShow)\n\(resultTable)")
Optional([["filename": "foo.jpeg", "state": "1"],["filename": "bar.jpeg", "state": "1"]])
Optional(<NSTableView: 0x101203070>)

@IBOutlet weak var resultTable: NSTableView! ,在包含类/NSView 的文件顶部,表明它已连接,MainMenu.XIB—ResultTable .

我想出了以下代码,试图从我的自定义 class View: NSView { 中显示 NSTableView 中的数据。

override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
    <...>
    func numberOfRowsInTableView(in tableView: NSTableView) -> Int {
        return fileDataToShow?.count ?? 0
    }
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
        var result:NSTableCellView
        result  = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self) as! NSTableCellView
        result.textField?.stringValue = fileDataToShow?[row][(tableColumn?.identifier.rawValue)!]! as! String
        return result
    }

    resultTable?.beginUpdates()
    // print(type(of:fileDataToShow)) // Optional<Array<Dictionary<String, Any>>>
    resultTable.insertRows(at: IndexSet(integer: fileDataToShow?.count ?? 0), withAnimation: .effectFade)    
    resultTable.reloadData()
    resultTable?.endUpdates()
}

fileDataToShow的内容没问题,但是其他代码行,.beginUpdates()/.insertRows(..等似乎没有任何 Action 。 如前所述,我无法弄清楚,也不知道在哪里或如何弄清楚...有人给我一些提示和/或指示吗?

我已经在 fileDataToShow 中定义了所有的键对应于 Identifiers在我的 XIB 中。

希望我已经成功地解释了我的问题。

编辑: 当我运行我的应用程序时,调试区域给出以下输出: *** Illegal NSTableView data source (<NSObject: 0x600000000b90>). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:

EDIT2/更新: 谢谢@vadian,但我还是没能解决这个问题,这里有一些更新。 这是我的整个文件,DropZone.swift : ```

class DropView: NSView/*, NSTableViewDataSource, NSTableViewDelegate*/ {
    @IBOutlet weak var resultTable: NSTableView!

    let dropZoneEnteredBackgroundColor = CGColor(red: 165/255, green: 165/255, blue: 165/255, alpha: 1.0)
    let dropZoneExitedBackgroundColor = CGColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1.0)//NSColor.gray.cgColor

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        self.wantsLayer = true
        self.layer?.backgroundColor = dropZoneExitedBackgroundColor
        registerForDraggedTypes([NSPasteboard.PasteboardType.URL,
                                 NSPasteboard.PasteboardType.fileURL])
    }
    override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        self.layer?.backgroundColor = dropZoneEnteredBackgroundColor

        return .copy
    }
    override func draggingEnded(_ sender: NSDraggingInfo) {
        self.layer?.backgroundColor = dropZoneExitedBackgroundColor
    }
    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        guard let pasteboard = sender.draggingPasteboard.propertyList(forType:
            NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")) as?
            NSArray else {return false}

        var droppedItems: [String: String] = [:]

        for path in pasteboard {
            guard let fullPath = path as? String else { return false }

            let fileManager = FileManager.default
            var isDir: ObjCBool = false

            if fileManager.fileExists(atPath: fullPath, isDirectory:&isDir) {
                if isDir.boolValue {
                    // the dropped item exists and it's a directory
                    droppedItems[path as! String] = "folder"
                }
                else {
                    // file exists and it's not a directory, hence a normal file
                    droppedItems[path as! String] = "file"
                }
            }
        }
        do {
            var fileDataToShow = [[String:Any]]()

            for object in droppedItems {
                if object.value == "file" {
                    do {
                        //let fullPath = object.key
                        let attributes = try object.key.extendedAttributes()  // Array<String>
                        let filename = object.key.fileName() + "." + object.key.fileExtension()

                        fileDataToShow.append(["state": "1",
                                               "filename": filename,
                                               "metadata":removed_attributes
                        ])
                    }
                    catch  {
                        debugPrint("Error info: \(error)")
                    }
                }
                else if object.value == "folder" {
                    // TODO
                }
            }

            func numberOfRows(in tableView: NSTableView) -> Int {
                return fileDataToShow.count
            }
            func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
                let cell = tableView.makeView(withIdentifier: tableColumn?.identifier ?? NSUserInterfaceItemIdentifier(rawValue: ""), owner: self) as! NSTableCellView
                // This line could crash if there values which are not String
                cell.textField?.stringValue = fileDataToShow[row][tableColumn?.identifier.rawValue ?? ""] as! String
                return cell
            }
            let insertionIndex = fileDataToShow.count
            //debugPrint(resultTable) // Optional(<NSTableView: 0x10100ba10>)
            //debugPrint(fileDataToShow) // [["filename": "img1.jpeg", "metadata": ["com.apple.metadata..", "com.a..."], "state": "1"]]
            resultTable.insertRows(at: IndexSet(integer: insertionIndex), withAnimation: .effectGap)
        } // do
        return true
    }
}

现在出现以下错误: *** Canceling drag because exception 'NSTableViewException' (reason 'NSTableView error inserting/removing/moving row 2 (numberOfRows: 0).') was raised during a dragging session

抱歉,自@vadian 上次回复以来我遇到了麻烦,所以不得不再次询问。 我做错了什么?

编辑 3: 感谢您的回答,@vadian,但我不明白。我有地方 numberOfRowstableView init 函数下面的函数。并在 do block 的最后执行以下代码,以尝试更新表:

resultTable.beginUpdates()
var i = 0
for row in fileDataToShow {
    print("state:",row["state"]!) // 1
    print("filename:",row["filename"]!) // file.jpg
    print("metadata:",row["metadata"]!) // ["com.apple.metadata..", "com.a..."]
    resultTable.insertRows(at: IndexSet(integer: i), withAnimation: .effectFade)
    i += 1
}
resultTable.endUpdates()

表中添加了新行,但它们都是空的。我如何 - 以任何方式 - 绑定(bind) fileDataToShow反对resultTable.insertRows .

如果你理解我糟糕的拼写和挑剔的问题 :) Swift 很难但学起来很有趣!

最佳答案

代码中有很多问题。

  • numberOfRowsInTableView 在 Swift 3+ 中是 numberOfRows(in tableView:
  • 数据源/委托(delegate)方法必须在类中的顶层,而不是在 performDragOperation 中。
  • 您使用了太多问号。
  • 不要将数据源数组声明为可选,声明为空非可选数组。

    var fileDataToShow = [[String:Any]]()
    

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

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
    let cell = tableView.makeView(withIdentifier: tableColumn.identifier!, owner: self) as! NSTableCellView
    // This line could crash if there values which are not String
    cell.textField?.stringValue = fileDataToShow[row][tableColumn.identifier!.rawValue)] as! String
    cell result
}

要插入带有动画的行,请不要调用 reloadData()。获取数组的最后一个索引,将项目附加到数组并插入行。
Begin-/endUpdates 没用

let insertionIndex = fileDataToShow.count
fileDataToShow.append([:]) // append some suitable dictionary
resultTable.insertRows(at: IndexSet(integer: insertionIndex), withAnimation: .effectGap)    

关于swift - 如何正确地向 NSTableView 添加一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53251780/

相关文章:

objective-c - 是否可以通过编程方式从我的 Mac 发送短信?

objective-c - 在 NSTableView 中重新加载数据,但保留当前选择

swift - Firebase - 按嵌套键查询

swift - 如何在 Swift 中关闭没有按钮或交互的 UIAlert?

xcode - 如何更改扩展坞和应用程序切换器中 OSX 应用程序的名称?

java - Java VM 上的 Android 错误。运行 Java SE 7

ios - UICollectionView 自定义 header 不显示 Swift 4

swift - 在 Swift 中将 NSData 添加到 UIActivityViewController 电子邮件

cocoa - Cocoa 的 NSTableView 中没有 Command 键的多重选择

objective-c - 不断更新TableView中单元格内的数据