1

Following a solution here, I was able to reorder the rows of a NSTableView, but the application doesn't save/load the table view with the new row order after quitting.

The view controller:

import Cocoa

@objcMembers class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet weak var tableView: NSTableView!

var persons = [Person]()


override func viewDidLoad() {
    super.viewDidLoad()
    persons = Person.loadFromFile()!


    tableView.delegate = self
    tableView.dataSource = self
    tableView.registerForDraggedTypes([.string])
}


override func viewDidDisappear() {
    Person.saveToFile(persons: persons)

}

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

func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
    return persons[row]
}


func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
    let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes)
    pboard.declareTypes([.string], owner: self)
    pboard.setData(data, forType: .string)
    return true
}

func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
    if dropOperation == .above {
        return .move
    } else {
        return []
    }

}

func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
    let pasteboard = info.draggingPasteboard()
    let pasteboardData = pasteboard.data(forType: .string)

    if let pasteboardData = pasteboardData {

        if let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: pasteboardData) as? IndexSet {
            var oldIndexOffset = 0
            var newIndexOffset = 0

            for oldIndex in rowIndexes {

                if oldIndex < row {
                    // Dont' forget to update model

                    tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
                    oldIndexOffset -= 1
                } else {
                    // Dont' forget to update model

                    tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
                    newIndexOffset += 1
                }
            }
        }
    }

    return true
}

The model:

import Cocoa

@objcMembers class Person: NSObject, Codable {

[...]

    static let DocumentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.appendingPathComponent("persons").appendingPathExtension("plist")

[...]

    static func loadFromFile() -> [Person]?  {
        guard let codedPersons = try? Data(contentsOf: ArchiveURL) else {return nil}
        let decoder = PropertyListDecoder()
        return try? decoder.decode(Array<Person>.self, from: codedPersons)
    }


    static func saveToFile(Persons: [Person]) {
        let encoder = PropertyListEncoder()
        let codedPersons = try? encoder.encode(persons)
        try? codedPersons?.write(to: ArchiveURL)
    }
}

Do I have to modify persons where the // Dont' forget to update model comments are?

EDIT: I was able to save/load the reordering of the table by removing then inserting an element in persons in each of the // Dont' forget to update model scopes.

Thank you.

Tommy V.
  • 67
  • 7
  • Hi @Tommy V In order to get the table view arranged in the order you want, you just need to use that order in your data source array. I'm not sure if I understood this part "ut the application doesn't save/load the table view with the new order". If you have the same datasource then it should load the same data using the same order. – Gabriel Goncalves Oct 12 '17 at 18:03
  • Hi Gabox. What I mean is that when I drag and drop to reorder the rows, close the application and then build and run it again, the reordering is not taken into account. When `viewDidDisappear` is called, the elements in `persons` are in the same order than before the reordering, unlike when I sort the rows using `sortDescriptorsDidChange` or insert a row with a button. – Tommy V. Oct 12 '17 at 18:30
  • 1
    That certarinly won't do the job because you don't store information on each record as to its place in the list. Create an element in the Person class that stores that remembers the place of a record in the list. – El Tomato Oct 12 '17 at 21:20
  • I was able to save/load the reordering of the table by removing then inserting an element in `persons` in each of the `// Dont' forget to update model` scopes. Is it what you told me to do, or is there another/better approach (as you told me to modify the `Person` class)? – Tommy V. Oct 13 '17 at 06:24

0 Answers0