Как правильно добавить строку в NSTableView - PullRequest
0 голосов
/ 11 ноября 2018

Во-первых, я должен отметить, что это мое первое GUI-приложение (XIB) в Swift, другими словами, я работаю и пытаюсь изучить разработку программного обеспечения для Swift и MacOS. Я просмотрел несколько вопросов, здесь, в стеке, а также документацию Apple по NSTableView, но я застрял.

Попытка сделать простое приложение для чтения некоторых атрибутов выбранных файлов. У меня есть пользовательский 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.

Я придумал следующий код, пытаясь отобразить данные в NSTableView, из моего пользовательского class View: NSView {

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, чтобы они соответствовали Identifiers в моем XIB.

Надеюсь, мне удалось объяснить мою проблему в порядке.

EDIT: Область Debug выдает следующий вывод при запуске моего приложения: *** Illegal NSTableView data source (<NSObject: 0x600000000b90>). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:

EDIT2 / Update: Спасибо @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, но я не понимаю этого. У меня есть функции numberOfRows и tableView прямо под функцией 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()

Новые строки добавляются в таблицу, но все они пусты. Как мне - никак - связать fileDataToShow с resultTable.insertRows.

Если вы понимаете мою плохую орфографию и суетливые вопросы :) Свифт тяжело, но учиться весело!

1 Ответ

0 голосов
/ 11 ноября 2018

В коде много проблем.

  • numberOfRowsInTableView - это numberOfRows(in tableView: в Swift 3 +.
  • Методы источника данных / делегата должны находиться на верхнем уровне вкласс, а не в 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)    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...