如何正确地将行添加到NSTableView

时间:2018-11-11 18:19:28

标签: 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>)

包含类/ NSView的文件顶部的@IBOutlet weak var resultTable: NSTableView!显示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(..等似乎没有任何作用。 如前所述,我无法解决这个问题,也不知道在哪里或如何解决...任何人都可以给我一些提示和/或指示?

我已经定义了fileDataToShow中的所有键以与XIB中的Identifiers相对应。

希望我能以一种好的方式来解释我的问题。

编辑: 运行我的应用程序时,Debug区域提供以下输出: *** 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块中最后执行了以下代码,以尝试更新表:

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()

新行已添加到表中,但它们都是空的。我如何以任何方式将fileDataToShowresultTable.insertRows绑定。

如果您了解我的拼写不佳和挑剔的问题:) Swift很难,但是学习很有趣!

1 个答案:

答案 0 :(得分:1)

代码中有很多问题。

  • numberOfRowsInTableView在Swift 3+中是numberOfRows(in tableView:
  • 数据源/委托方法必须位于类的顶层,而不是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)