NSButtonCell in NSCell-based NSTableView & Bindings - objective-c

I have an NSButtonCell as data cell in one of my NSTableView's columns. The table view is cell-based and has two columns ; one with the button cell, one with a text field cell.
The button cell's respective column's target and argument are bound to my app delegate (as the target) and to the selectedObjects array of an NSArrayController (as the argument).
Basically the same as what's outlined in one of Apple's Technical Q&As.
When I open the app, the table view is automatically populated. Everything works as expected. If I click a button my action method gets called just fine, and this method receives the selectedObjects as a parameter.
It's just not the functionality I want ; I'd like to get the object that is represented by the row of the clicked NSButtonCell instead of the currently selected objects (in my case this is an NSString as my NSArrayController is populated with strings).
Is it possible to get this particular object, only through bindings? Or should I, instead, set the selector of the NSButtonCell (the data cell) without bindings? And then get the object by using NSTableView's -clickedRow method?

Related

Detect which NSTableView is active

I have an NSSplitView showing two NSTableView instances. I need to detect which table view has become "active" (of focused), which means the one that the user has clicked. I need to know that because each table view acts as a source list for another view that shows the content of the selected row(s). This other view is shared for both tables.
I could do it by subclassing NSTableView and reacting to mouseDown: or another method but it I'd rather avoid subclassing just for that. I also don't want to track any NSWindow event just to know if the user has clicked one of the tables (I'd rather subclass NSTableView).
Currently, I use the delegate method tableViewSelectionDidChange:, but this method is, obviously, only called when the selected row changes. I need to know that a table becomes active even if the selected row hasn't changed.
Observing the clickedRow property of the table views doesn't appear to work. If may not be KVO compliant.
Any ideas?
For those interested, the most convenient solution I found was to take advantage of the fact that NSTableView is a subclass of NSControl. So just like NSButton it can send action messages when clicked (upon mouse up).
For each tableView, I wired its "action" to the same ibaction selector of my controller object in interface builder.
The controller identifies the sender and acts accordingly.
No need to subclass NSTableView.

Cocoa- Using representedObject for NSButton in NSCollectionView

Setup
I have a NSCollectionView. I have a checkbox in the View Prototype. I've successfully set up bindings so the Card Title and action get populated. (image 1, below)
Goal
I'd like, when I click the checkbox, to run a function that accesses the specific CardModel that the View Prototype is already able to access. I'll then manipulate its data accordingly.
Research
I found this article on SO: Get the representedObject values of NSCollectionViewItem NSButton click, which describes my situation pretty well. The answer, unfortunately, is without specific code. Here's what's suggested:
So, first, set the represented object of your button's cell to the
collection view item that owns the button. (You can do this in the nib
editor.) Then, in your action method, get the button's cell, then the
cell's represented object (which is the item), then the item's
represented object.
Seems simple enough, right?
Attempted Solution(s)
I create a method cardCheckBoxClicked: and connect it to the checkbox.
As per the advice above, I connect the button cell's outlet representedObject to Card Collection View Item. (image 2)
I then attempt to get the Card Collection View Item's representedObject in code.
From MainWindowController.h:
-(IBAction)cardCheckBoxClicked:(id)sender
{
CardModel* cModel = [[sender representedObject] representedObject];
NSLog(#"card title: %#",cModel.title);
}
Error
When I click on the checkbox, I get the following error:
-[NSButton representedObject]: unrecognized selector sent to instance 0x6080001581b0
Question!
So - how do I access the button cell's represented object? Did I misunderstand the advice given above? How can I successfully access the data I need?
Images (reference)
bindings example
represented object connection
This here:
-[NSButton representedObject]:
Is you asking the Class NSButton to run the method representedObject. Make sure you distinguish between a Class an an object or instance of that class.
You need to take the actual button, get its button cell, (at least I think that's what you want), and then call representedObject on the cell. If I am understanding you correctly. I never touch interface builder, so here's completely made up code that lines up with what you are asking for.
someObject = [[theButton cell] representedObject];
In addition to CH Buckingham's answer, you should also consider using bindings. You can bind the checkbox's value binding to the collection view item with a model key path which goes through representedObject to some property on your CardModel. (If desired, the key path can keep going through your model object graph.) That will set that property whenever the button is toggled.

NSArrayController selection not updating with NSTableView binding

This has been befuddling me for hours...
I have an object (CuesDoc) with a property (NSMutableArray *cuesArray) and some other properties. The cues array holds multiple Cue objects. There is a property called (CuesDoc*) currentCuesDoc in my AppDelegate.
In IB, I have an NSArrayController, which is bound to the AppDelegate.currentCuesDoc.cuesArray.
I have a view-based NSTableView which is bound to the NSArrayController and can add/remove/edit values in the table and cuesArray. So far so good.
I have detail fields below that, which are bound to NSArrayController.selection, with the model key path set to each property.
When the view first appears, the detail fields populate with the contents of the first item in the table view, however when I select other rows, the detail fields do not update to reflect the current selection.
I added an observer to selectionIndexes and selection for the NSArrayController, and when the view appears, I get called for the observeValueForKeyPath: method once, but not after changing selections.
For view-based NSTableViews, you must bind the tableView's selection indexes to the array controller key of selectionIndexes to keep the view's selection in sync with the controller's selection. Selection bindings are separate from content bindings. The older, cell-based NSTableView APIs did not require this step.

Custom NSPopUpButtonCell outlet / bindings

I'm having a problem with a custom NSPopUpButtonCell in a table that's instantiated when the table view is populated via bindings and a NSArrayController.
The pop up button cell is created but when attempting to access the outlet by overriding the pop up button cell's setMenuItem:item method it's nil.
Is this the expected behaviour..?
Should another method be used to replace the menu at creation time?
Basically I need the outlet to link back to my controller (NSWindowController) for that document window so I can customize the NSPopUpButtonCell menu accordingly from the custom popup button when it's populated.
A solution using bindings would be even better - but when overriding setObjectValue: I can see it's only never called with a nil parameter.. using a stock NSPopUpButtonCell results in a properly populated pop up menu, though.
(see also Why is NSPopUpButtonCell showing correctly when only setObjectValue:nil is called).
You don't need to override anything to populate an NSPopUpButtonCell in an NSTableView column. The thing to know is that you set the bindings on the NSTableColumn and not on the cell itself. Typically, you would have an NSArrayController in your xib that is bound to an NSArray containing all the options for the pop-up, and then you would select the column with the pop-up cell and go to it's bindings. Like in this screenshot (note the populated Content, Content Objects, and Selected Object bindings in the inspector on the right):
If you want a working example, you can check out this project I whipped up for another StackOverflow question. There's a bunch of unrelated stuff pertaining to making the NSPopUpButtonCell use NSAttributedStrings, but the bindings in the xib constitute a working example of how to bind an NSTableColumn with a pop-up whose options are populated by bindings.

delete UITableViewCell programmatically

I was wondering if it's possible to remove empty cells (empty = cells with no textLabel) after all the cells are created in a UITableView.
Why do you have empty cells? Are you using a consistent technique to control both the number of cells in your table and the content of those cells?
If you're using a UITableViewController, then your controller is automatically declared as the tableview's datasource. If you're using a UIViewController, then you'll declare it as comforming to the UITableViewDataSource protocol (and connect it up in Interface Builder).
Either way, as the tableview's datasource, your controller is required to implement two methods:
– tableView:cellForRowAtIndexPath:
– tableView:numberOfRowsInSection:
Presumably you're providing the data for the tableview with an array or other means inside -tableview:cellForRowAtIndexPath:. Inside this method the cell's label will be set from an entry in your array. And inside tableView:numberOfRowsInSection: you'll be doing something like [myArray count] to return the number of cells. tableview:cellForRowAtIndexPath will be called as many times as you tell it to (dictated by what you provide in tableview:numberOfRowsInSection:). If the datasource array changes, and you'd like to reflect the changes in your tableview, then you can call
[self.tableview reloadData]; //if inside a UITableViewController
[self.myTableViewOutlet reloadData]; //if inside a UIViewController
Note that reloadData reloads the entire tableview, so in some cases this may be computationally expensive. In this case, instead of calling reloadData you can focus on individual rows with the method: deleteRowsAtIndexPaths:withRowAnimation: (see UITableView Class Reference)
As the app delegate, you are responsible for providing cells. It is your responsibility to return every cell, and the number of cells in the table. Therefore as the app delegate you should have a means (be it by NSMutableArray or otherwise) to mutate the data that you return to the table view.