0

I have constructed a view-based NSTableView, programmatically (code-only, without XIB) following this, which is backed by an NSArrayController, created simply by: var controller = NSArrayController() and bound to the table by:

tableView.bind(NSContentBinding, toObject: controller, withKeyPath: "arrangedObjects", options: nil)
tableView.bind(NSSelectionIndexesBinding, toObject: controller, withKeyPath:"selectionIndexes", options: nil)
tableView.bind(NSSortDescriptorsBinding, toObject: controller, withKeyPath: "sortDescriptors", options: nil)

I only use the controller's methods to add/remove objects, and get them by: (controller.arrangedObjects as! NSArray)[index] . The items in the array are of a Class with String? or dynamic Double fields and they extend from NSObject.

Adding and removing rows inside the model automatically adds/removes the corresponding rows in the table, but I cannot bind the cell views to the items -and observe changes in their fields-. I tried to follow this and this with no luck. Final version of my cell view is like:

class PLTextCellView: NSTextField {
    init(columnName:String) {
        //.... erased...
        self.columnName = columnName
    }
    var columnName = ""

    var displayText:AnyObject {
        get {
            return stringValue
        }
        set(val) {
            stringValue = String(val)
        }
    }

    weak
    var item:PLItem? = nil

    override
    var objectValue: AnyObject? {
        didSet {
            item = objectValue as? PLItem
            if (item != nil) {
                Swift.print("item to bind is:\(item) ")
                unbind("displayText")
//                bind("displayText", toObject: self.item!, withKeyPath: columnName, options: [NSNullPlaceholderBindingOption:"--"])
                bind("displayText", toObject: self, withKeyPath: "objectValue."+columnName, options: [NSNullPlaceholderBindingOption:"--"])
            } else {
                Swift.print("unbinding... ")
                unbind("displayText")
            }
        }
    }

    deinit {
        unbind("displayText")
    }

    override
    func prepareForReuse() {
        unbind("displayText")
        super.prepareForReuse()
    }
    ///... erased ....
}

If I don't use any binding, I only get item object ids displayed on the table on each and every column, even if I explicitly set displayText (so something must be internally setting the text !!)

Question 1: If I use the commented/first bind line, each item object prints a different object id to the console -even if some represent the same object- and none of them exist in the controller! Why ?

Question 2: If I use the uncommented/second bind line, I get this error: An instance of ...item... was deallocated while key value observers were still registered with it.. This says I should not do binding/observing in the cell view, but the most convenient place seems the cell view (for MVC). I don't know a way to remove all -view-bindings in the model items in their deinits. (Interestingly, I even don't delete any items from the controller).

As I understand from the related questions and answers, this question also holds for UITableView's. So, what is the proper way to do the binding ?

Community
  • 1
  • 1
user3648895
  • 395
  • 10
  • 20
  • Is there any reason you're using an ``NSTextField`` subclass as your *cell*? Why don't you want to use the ``NSTableCellView`` class? – Paul Patterson Jun 27 '16 at 10:54
  • In its header file it had just 2 fields: a text and an image. If I can attach any subview in it and can make this binding problem solved with it, why not? – user3648895 Jun 27 '16 at 11:00
  • 1
    Before you start doing unconventional things in your code, make sure that you can get things working in a more conventional set-upfirst. It's pretty rare to use a non-``NSTableCellView`` subclass as your cell view (I've never done it) so I'd advise you to avoid this if you can. This link - http://stackoverflow.com/questions/33992756/is-it-possible-to-create-a-view-based-nstableview-purely-in-code/34131404#34131404 - takes you to a question that describes how to set up a table-view in code (note also the link to a demo project in one of the comments) – Paul Patterson Jun 27 '16 at 11:14
  • @PaulPatterson Extending from `NSTableCellView` had no effect. Modified your demo a bit to reflect my scenario; it is [here](http://jmp.sh/b/XaTptb0b2qboJ46g7Nll). All the information is included in the comment block at the beginning of the source file(AppDelegate). I believe you'll understand what I'm trying to achieve, in just a few minutes by reading it and running the project. – user3648895 Jun 28 '16 at 08:29
  • **Mistake 1**: if you're using an array controller to provide the table-view with its data you don't need a data-source (the array controller is the data-source). **Mistake 2**: the fields you're binding to must be marked dynamic otherwise any changes you make to them won't be noticed by whatever is observing them. **Mistake 3**: the binding you set up uses an incomplete key-path - it should point to the property (e.g. ``val1``), not the instance (``Item``). In short you need to read the ``KVO`` and ``KVC`` guides - these are the technologies that underpin bindings. (http://jmp.sh/LEWLUmS) – Paul Patterson Jun 28 '16 at 10:20
  • @PaulPatterson, correcting Mistakes 2 & 3 solved this problem; I will gratefully accept as an answer if you paste them. For Mistake 1: I am implementing Drag&Drop, I made some Google'ing but could not find any alternative solution other than using a dataSource and implementing (only) Drag&Drop related methods, unless you know of another solution? I also have (another) performance related question below: – user3648895 Jun 29 '16 at 06:07
  • @PaulPatterson, I made a test for cell reuse/creation (uploaded [here](http://jmp.sh/b/1FVUAWHb0XzEKa0tXBjy), if you want to test it yourself): Double clicking on the table adds a new row to the table. After row number _(number of visible rows)*2_ {alias: _2v#r_ }, each row is created, meaning the table only/always keeps _2v#r*(number of columns)_ cell views, the rest is always re-created (cell creation code is run for the rest ( _(all rows in the table)-2v#r)_ rows). Is this the normal behavior, and logical performance-wise ? – user3648895 Jun 29 '16 at 06:17

0 Answers0