Cocoa - multiple view-based NSTableViews - objective-c

I am just beginning to teach myself cocoa, and I am running into a (probably simple) issue displaying multiple view-based NSTableViews with same delegate and controller (the App Delegate, in my case). I saw this post:
Best way to handle multiple NSTableView(s)
but the method described still gives me errors - specifically
Duplicate declaration of method 'numberOfRowsInTableView:'
Duplicate declaration of method 'tableView:viewForTableColumn:row:'
Obviously, the compiler isn't seeing that the different method declarations are for different table views.
The code for the tableviews in the AppDelegate.m file is
#synthesize tableView1;
#synthesize tableView2;
-(NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView1
{
return 1;
}
-(NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView2
{
return 2;
}
- (NSView *)tableView:(NSTableView *)tableView1 viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTableCellView *resultForTable1 = [tableView1 makeViewWithIdentifier:tableColumn.identifier owner:self];
resultForTable1.textField.stringValue = #"This should appear in the first tableView";
return resultForTable1;
}
- (NSView *)tableView:(NSTableView *)tableView2 viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTableCellView *resultForTable2 = [tableView2 makeViewWithIdentifier:tableColumn.identifier owner:self];
resultForTable2.textField.stringValue = #"This should appear in the second tableView";
return resultForTable2;
}
and in my AppDelegate.h file, I have:
#property (weak) IBOutlet NSTableView *tableView1;
#property (weak) IBOutlet NSTableView *tableView2;
What am I doing wrong here?

I think you're misunderstanding the method described in that answer.
You're getting a compiler error because you are trying to implement the same method twice. The following would all be implementations of the same method:
- (void)setBlah:(id)aBlah {
- (void)setBlah:(id)newBlah {
- (void)setBlah:(id)theNewBlah {
The different "names" given to the parameter that follows the (id) parameter type are only local to the implementation block of that method.
You should be able to accomplish what you want to do using code like the following:
#synthesize tableView1;
#synthesize tableView2;
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
if (aTableView == tableView1) return 1;
else if (aTableView == tableView2) return 2;
return 0;
}
- (NSView *)tableView:(NSTableView *)aTableView
viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *tableCellView = [aTableView
makeViewWithIdentifier:tableColumn.identifier owner:self];
if (aTableView == tableView1) {
tableCellView.textField.stringValue =
#"This should appear in the first tableView";
} else if (aTableView == tableView2) {
tableCellView.textField.stringValue =
#"This should appear in the second tableView";
}
return tableCellView;
}
Notice that I made sure to name the parameter aTableView, something different from the instance variables, so that I can successfully compare it to the instance variables in the following lines.

You don't duplicate the methods multiple times -- you're not supplying the argument, "tableView1", tableView2", etc., the table views call these methods and send themselves as the argument. So, if you want to use the same delegate for multiple tables, you put if statements in the delegate methods, to ascertain which table sent the message. Declare an IBOutlet for each table and then do (in pseudo code) if table1 .... else if table2 ...etc.

Related

cocoa-How to use tableViewSelectionDidChange:?

I have a NSTableview in my view and I want to do some other thing when the user select a particular row. I tried tableViewSelectionDidChange method but it seemed not working.
-(void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger row = [self.InfoTable selectedRow];
if (row == -1) {
return;
}else{
self.NumberInputTextField.stringValue = studentsInTable[row][0];
self.NameInputTextField.stringValue = studentsInTable[row][1];
self.ClassnumberInputTextField.stringValue = studentsInTable[row][1];
}
}
and I have
#interface ViewController : NSViewController <NSTableViewDelegate,NSTableViewDataSource>
and
self.InfoTable.dataSource = self;
self.InfoTable.dataSource = self;
Also, I've googled but haven't found a useful answer.
Can any one give me a hint on it?
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification is delegate method but you are only setting dataSource to self.
[self.InfoTable setDelegate:self];
If you have a cell-based or view-based NSTableView then nothing as such is required. Just make sure the tableView's delegate is set to the controller class, and you implement them.
Or you can do the above with codes:
self.InfoTable.dataSource = self;
self.InfoTable.delegate = self; //Note you used dataSource twice
And make sure you implement these methods if you are not doing binding to load the tableView.
- (void)tableViewSelectionDidChange:(NSNotification *)notification{
NSLog(#"Your seleceted a row...");
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.anArray count];
}
- (id) tableView:(NSTableView *)TableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row {
return self.anArray[row];
}

*** Illegal NSTableView data source (<NSView: 0x102535290>). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:

OK, this is driving me crazy, i have an Xcode OSX App that i have been working on. I made some changes recently and i have started getting the following error at compile time:
iModerate Desktop[72478:303] *** Illegal NSTableView data source (<NSView: 0x102535290>).
Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:
I cannot workout where this is coming from, i have implemented both these methods in my appDelegate:
- (NSView *)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
// The return value is typed as (id) because it will return a string in most cases.
id returnValue=nil;
// The column identifier string is the easiest way to identify a table column.
NSString *columnIdentifer = [tableColumn identifier];
// Get the name at the specified row in namesArray
NSString *theName = [[self.twitterClientsController arrangedObjects] objectAtIndex:row];
// Compare each column identifier and set the return value to
// the Person field value appropriate for the column.
if ([columnIdentifer isEqualToString:#"name"]) {
returnValue = theName;
}
return returnValue;
}
and this
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return[[self.twitterClientsController arrangedObjects] count];
}
And app delegate is set as NSTableViewDelegate
Now the extra weird/frustrating thing, is that i have no NSTableView in the xib, i did, but i have deleted them all. I have event opened the XIB in BBedit and searched for NSTableView and there is 100% not one in there!
So, help please! If i could work out what NSView: 0x102535290 is i could maybe track this down.
Help to save my sanity greatly appreciated!
Gareth
it is solved with me that way :
remove the datasource and the delegate connections from the interfaceBuilder.
make an outlet property for the your tableview in the .h file
in the .m file in applicationDidFinishLaunching method set the delegate and datasource manually for your tableview
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];

NSButtonCell checkbox inside NSTableView is always getting NSOffState. Why?

Hi guys I'm new to Cocoa programming and I am getting always NSOffState whether I'm checking or unchecking an NSButtonCell (Check Box Cell in the UI dragged to a cell in an NSTableView).
I have a #property IBOutlet NSButtonCell *mySelection, connected to the respective UI and the following code.
- (void) tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *media = [mediasRecesEnStock objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"seleccion"])
{
if ([mySelection state] == NSOnState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"on state");
}
if ([mySelection state] == NSOffState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"off state");
}
}
}
I never get the NSOnState to execute, the only NSLog message I get is: off state.
Can anyone give me some help?
Thanks!!
If you have one outlet ("mySelection") and multiple rows, which row did you think the outlet connects to? (Answer: none of them. You probably hooked it up to the prototype cell, which is never displayed or used directly.)
But no matter, you don't need to check the state before you set it. Assuming your other code is correct, you should be able to do something like:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *medium = [mediasRecesEnStock objectAtIndex:row];
if ([tableColumn.identifier isEqualToString:#"seleccion"])
medium.seleccion = object.booleanValue;
}
Less code is better code.

Setting up a UITableView's subclass as it's delegate/dataSource

I have a subclassed UITableView, 'subclassChild' which inherits from another subclass 'subclassParent'. 'subclassParent' then inherits from UITableView.
My view controller inherits from a custom View Controller 'GenericTableViewController', and contains my UITableViewController which has the class of 'subclassChild'.
'GenericTableViewController' customises my tables header and footers for all view controllers containing a table view.
My question is, how do I make it so that my UITableView can use 'subclassChild' and 'subclassParent' as its delegate and dataSource. Still making sure that 'GenericTableViewController' customises my tables header and footers.
Sorry if this is a bit confusing, I've tried to describe it as well as I can.
P.S I have added <UITabBarDelegate, UITableViewDataSource> in the header files.
And I have made sure that the table inherits from 'subclassChild' in IB.
Thanks in advance!
What you can do, is have your 'GenericTableViewController' have a delegate itself. It will do most of the work itself, but at the end it shall ask its delegate 'anything else to do'?
And when the delegate says 'Yes, there is something actually', that code will get executed, serving as data source, delegate or whatever you want it to be. As an example (please forgive me for code inaccuracies):
- (void) rowForIndex:(NSInteger)row {
if([delegate respondsToSelector:#selector("TVGetRow:")]) {
return [delegate TVGetRow:row]
} else return Rows[row];
}
This example assumes, that you have an array of tableview cells, but should give you a good idea. You don't need to implement UITableViewDataSource to that subclass header files, but instead use a custom protocol.
Edit: Since apparently I was unclear in what I was aiming to do, allow me to add a little information.
The basic idea is to use 'subclassChild' and through it 'subclassParent' as delegates for GenericTableViewController, instead of using 'subclassChild' as the type for the tableView. (You should never subclass a view, unless you intend to change it's drawing behaviour).
The general hierarchy would work like this:
subclassChild -> delegate -> GenericTableViewController <- DataSource/Delegate <- TableView
You'd need to create an init method, or a setter method, which sets this delegate, and a simple protocol, which allows you to call functions, like so:
#interface GenericTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate> {
id theDelegate;
}
- (id) initWithDelegate:(id)delegate;
#end
#protocol GTVC
#optional
- (UITableViewCell *) getDataForRow:(NSIndexPath *)path;
- (void) rowSelected:(NSIndexPath *) path;
#end
In the .m file, this example would look like this:
#implementation GenericTableViewController
- (id) initWithDelegate:(id)delegate {
self = [super initWithNibName:#"TableView.nib" bundle:nil];
if(self) {
theDelegate = [delegate retain]; //The 'retain' is optional, really.
}
return self;
}
- (void) dealloc {
[theDelegate release]; //In case of retain -> release.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if([theDelegate respondsToSelector:#selector(getDataForRow:)])
return [theDelegate getDataForRow:indexPath];
else {
//Create general UITableViewCell
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([theDelegate respondsToSelector:#selector(rowSelected:)])
[theDelegate rowSelected:indexPath];
else {
//Do stuff
}
}
#end
This protocol/delegate 'game' can be expanded easily, giving you all levels of control you need...all the while with the GenericTableViewController as a 'backup', if any delegate isn't set, or 'not ready' to answer the request for further instructions.
I hope this helps.

Delegate events for NSTextField in a view-based NSOutlineView?

I have a flawless functioning view-based NSOutlineView with a proper set-up datasource in my project. Now I want to allow the user to change certain entries. So I made the NSTextField in the IB editable. For a cell-based NSOutlineView you can use the delegate method outlineView:setObjectValue:forTableColumn:byItem: however it's not available for a view-based NSOutlineView as stated in the header file for the NSOutlineViewData protocol:
/* View Based OutlineView: This method is not applicable.
*/
(void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
So I searched for another delegate method and found outlineView:shouldEditTableColumn:item:. However this delegate method doesn't get fired. Probably because I'm not editing a cell.
So my question is: Is there any other way to notice when a row changed than having a delegate for each NSTextField?
You are correct that your text field needs to be editable in Interface Builder.
Next, make your controller conform to NSTextFieldDelegate. Then, set the delegate for the text field in outlineView:viewForTableColumn:item:, like so:
tableCellView.textField.delegate = self
Here's a simplified example, where you've implemented the method for returning the table cell view for an item for your outline view.
-(NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSTableCellView *tableCellView = [outlineView makeViewWithIdentifier:#"myTableCellView" owner:self];
MyItem *myItem = (MyItem *)item; // MyItem is just a pretend custom model object
tableCellView.delegate = self;
tableCellView.textField.stringValue = [myItem title];
tableCellView.textField.delegate = self;
return result;
}
Then, the controller should get a controlTextDidEndEditing notification:
- (void)controlTextDidEndEditing:(NSNotification *)obj
{
NSTextField *textField = [obj object];
NSString *newTitle = [textField stringValue];
NSUInteger row = [self.sidebarOutlineView rowForView:textField];
MyItem *myItem = [self.sidebarOutlineView itemAtRow:row];
myItem.name = newTitle;
}
Well, it seems like Apple wants us to use the delegate methods of each NSTextField as stated here:
This method is intended for use with cell-based table views, it must not be used with view-based table views. Instead target/action is used for each item in the view cell.
So there's currently no other way to do this.