Two NSTableViews in a single view - objective-c

I have two NSTableViews in a same view and they have the same data source and delegate.
The two table views are named as varTableView and constraintTableView. Now the number of rows in each is different and I I am finding it difficult to implement the two methods:
-(NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
-(id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Is there any way to identify the tableviews and return the corresponding number of rows?
My implementation was something like this:
-(NSInteger) numberOfRowsInTableView:(NSTableView *)tableView{
if([tableView.identifier isEqualToString:#"variableTableView"]){
return [self.arrayOfVariableNames count];
}
else{
return [self.arrayOfConstraintNames count];
}
}
it is always returning the count of constraints names but not the variable names

you can implement this either by using name of table or tag of table to know for which table particular delegate method is called for like
-(NSInteger) numberOfRowsInTableView:(NSTableView *)tableView -(id) tableView: (NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row: (NSInteger)row
{
if (tableView == varTableView)// or check table tag, first you need to set tag for table
{
// do something with varTableView
return ([array1 count]);
}
else if (tableView == constraintTableView)
{
// do something with constraintTableView
return ([array2 count]);
}
}

Yes. It's the tableView parameter in the methods you included.
if (tableView == varTableView)
{
// do something with varTableView
}
else if (tableView == constraintTableView)
{
// do something with constraintTableView
}
The number of rows tends to be the number of objects in the array serving as the data source.

As suggested in other answers you can differentiate between two tables by comparing its outlets. There s also another way but you would have to change your existing designs a bit.
Instead of adding two table views in one view controller, create a placeholder view for both of them. Create two table view controllers and add them to these placeholder views as subviews. This way you can have both tables in separate controllers and it will get rid of all the if else blocks which you will end up writing. It might slow you down initially but you will get benefitted later.

Related

View-based NSTableView yields odd results with bindings and delegate

I have an NSTableView, the content of which is bound to an arrayController using a filterPredicate. The tableView is view-based, so its delegate is set to an object that has the
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
method. I'm getting odd behavior, which may be related to my observation that this method is being called even when the controller's number of arrangedObjects is 0. Moreover, each time the method is called, the number of rows it asks for is the total number of objects in the array controller's content (as opposed to arrangedObjects). It think it may be because of this that when I set the objectValue of the cellView requested using
cellView.objectValue = [arrayController arrangedObjects] objectAtIndex:row];
that it is often the wrong one. Any ideas?
objectValueForTableColumn is not necessary for the case of bindings. Just banged my head against a wall for a few minutes and came back with this path of least resistance:
1) bind your NSOutlineView content to the NSTreeController (arrangedObjects controller key)
2) bind the subviews of each of the NSTableCellView to the enclosing NSTableCellView (objectValue.foo where foo is the key path for your target field)
3) set the identifier for each column in your outline view (I do this for tracking reordering of table columns between launches)
4) make sure each NSTableCellView has the identifier set to Automatic (or is the same as the table column) ** This is important and messed with me **
5) when you call makeViewWithIdentifier: make sure you pass tableColumn.identifier
This assumes you only have one cell view type per table column. Otherwise, you'll need to pass in the correct identifier for the cell view you want in makeViewWithIdentifier:.
Well, I still can't explain why the view was behaving as it was, but I did manage to get things to behave correctly, doing the following:
binding tableView content to arrayController's arrangedObjects
binding various elements of the tableCellview used in the tableView to the appropriate keyPath of the tableCellView's objectValue
providing each tableCellView as follows (where BILSelectableRoundedTableCellView happens to be a subclass of NSTableCellView):
(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSInteger numberOfObjects = [self.arrayController.arrangedObjects count];
if (numberOfObjects == 0)
return nil;
if (row >= numberOfObjects)
return nil;
BILSelectableRoundedTableCellView *viewForTableColumnRow = [self.myTableView makeViewWithIdentifier:#"DataCell" owner:self];
return viewForTableColumnRow; }
Finally, using a datasource that implements the following:
(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSInteger numberOfObjects = [self.arrayController.arrangedObjects count];
if (numberOfObjects == 0)
return nil;
if (row >= numberOfObjects)
return nil;
return [self.arrayController.arrangedObjects objectAtIndex:row]; }
The use of the later seems contrary to the documentation out there, but is necessary to get things to work properly. Interestingly, the other datasource method (numberOfRowsInTableView) is not called when using bindings, although it is called when i'm not using bindings.

Changing view for selected in view-based NSTableView

How do I change the view for the selected row when using a view-based NSTableView? Specifically, I'd like to have a simple NSView subclass for unselected rows and a more complex NSView subclass for the selected row which allows editing of more information associated with the row item.
An example is the way Things allows you to expand the item being edited as seen here: http://culturedcode.com/things/
My guess is that you want to use a different NSTableCellView subclass when the row is selected. I think you should be able to do something like this:
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSTableView *table = [notification object];
NSIndexSet *allColumns = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [[table tableColumns] count])];
[table reloadDataForRowIndexes:[table selectedRowIndexes] columnIndexes:allColumns];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if ([[tableView selectedRowIndexes] containsIndex:row])
{
// If the row is selected, return an instance of the class for selected views
SelectedTableCellView *selectedView = ...; // Get from makeViewWithIdentifier:
// Set up selectedView
return selectedView;
}
else
{
NonSelectedTableCellView *nonSelectedView = ...; // Get from makeViewWithIdentifier:
// Set up nonSelectedView
return nonSelectedView;
}
}
It might be nice if you elaborated a little bit more on what you mean by "change the view to a more complex view"
Nonetheless, you could for instance, implement - (void)tableViewSelectionDidChange:(NSNotification *)notification in the delegate of the table view, get the selected NSTableRowView if it is visible, and change it in what way you want, which includes making it more complex, expanding it (see below), etc.
To modify the size of a row, you would need to implement - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row in the same delegate, and call the table view's -noteHeightOfRowsWithIndexesChanged to update the height for particular rows.
I think the app is created by NSOutlineView in outlineview only you can easily expand your selected row...
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if ([item isKindOfClass:[NSDictionary class]])
{
return YES;
}else
{
return NO;
}
}
I think this way is write..

Adding Section Headers to UITableView

Forgive the noob question, still learning.
I have a UITableView with a single array for data (approx 50). I'm looking to implement section headers but I just can't get my head around it. All the information I can find seems to vary greatly (there seems to be manes ways it can be implemented?) and I can't seem to piece it all together!
As ever I can't make out the Apple docs but I think that's down to my inexperience.
So a few questions:
I understand I need to state how many sections are needed, with a single array, would would be the best way?
Can I use a single array? Or am I going to need to break it down into numerous arrays, each with their own section?
Thanks in advance.
Code for cellForRowAtIndexPath for jonkroll
switch (section) {
case 0:
cell.textLabel.text = (NSString*)[tableViewArray objectAtIndex:row];
break;
case 1:
cell.textLabel.text = (NSString*)[tableViewArray objectAtIndex:row+3];
break;
case 2:
cell.textLabel.text = (NSString*)[tableViewArray objectAtIndex:row+17];
break;
}
The methods in the UITableViewDataSource protocol let you to define how the tableView renders your data.
Use numberOfSectionsInTableView: to tell your table how many sections it will have.
Use numberOfRowsInSection: to tell your table how many rows will be in each section.
Use cellForRowAtIndexPath: to tell your table what to render in a particular cell based on indexPath (indexPath is a structure that identifies a particular cell based on section and row)
So you say you have a single array that you want to display in more than one section. This is a very contrived example, but let's say you want rows 1-30 to be in the first section and rows 31-50 to be in the second section.
You can do the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case 0: return 30; break;
case 1: return 20; break;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int section = [indexPath section];
int row = [indexPath row];
NSString* CellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
switch (section) {
case 0:
cell.textLabel.text = (NSString*)[array objectAtIndex:row];
break;
case 1:
cell.textLabel.text = (NSString*)[array objectAtIndex:row+30];
break;
}
return cell;
}
You application probably has an UITableViewDataSource and UITableViewDelegate (this is where you return the elements of the array and configure the number of sections, etc.). Within that class (or those classes if they're separate) there's two approaches you can take depending on what you'd like to do.
If you just want to have a title with the default look, implement tableView:titleForHeaderInSection: to return the title you want for each section.
If you want a custom view, implement tableView:viewForHeaderInSection: as well as tableView:heightForHeaderInSection: to return the custom view along with the height of the header views.
If you want a header consider using a UITableViewCustomCell, drag one into the nib where you have your tableView set up. Declare and link up in InterfaceBuilder.
IBOutlet UITableViewCell *cellOne;
#property (nonatomic, retain) IBOutlet UITableViewCell *cellOne;
Then you can use to assign the CellView to the Table view for your header.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([array count] == 0) return cellone;
You should look at the iPhoneCoreDataRecipes example that Apple puts out. They use a Tableview and show the data different ways. Being new at programming myself, I don't know how to provide the link to you, but it's in the iOS 5.0 Library under Data Management.

Understanding how different methods in OutlineView relate

I'm having trouble fully understanding all the different places you can return cells when using an OutlineView. As far as I can tell there are four places:
NSOutlineViewDataSource has:
outlineView:child
outlineView:objectValueForDataColumn
And NSOutlineViewDelegate has:
outlineView:willDisplayCell
outlineView:dataCellForTableColumn
If I have a outline view with different items, like the SourceList example, where do I do what and why? I have GroupItem headers and a tree of IconAndImage cells that subclass NSTextFieldCell. Where should these be instantiated and where should I set the styling, image and title?
What Cocoa means by the word cell is not the same as what you would call a cell in for example Excel.
In Cocoa, a cell is a NSCell subclass and could be considered as a light-weight reusable NSView. It is used to draw many items in the same way. E.g.
- (void)drawRect:(NSRect)draw_rect {
// ...
for ( id value in myDataArray ) {
[cell setObjectValue:value];
NSRect cellFrame = ...;
[cell drawWithFrame:cellFrame inView:self];
}
So a data source does not return cells, but instead return objects that are parameters to [(NSCell) -(void)setObjectValue:(id)value]. The delegate returns which cell-object to use for each item and should be implemented so that you only create each cell-type once. E.g.
- (NSCell *)outlineView:(NSOutlineView *)outlineView
dataCellForTableColumn:(NSTableColumn *)tableColumn
item:(id)item {
NSCell *cell = nil;
switch(tableColumn.tag) {
case 0:
if ( ! myCell ) {
myCell = [[NSCell alloc] init];
}
cell = myCell;
break;
default:
break;
}
return cell;
}
You should use table column tags or a similar feature to handle column re-ordering by the user.

NSTableView not updating like I think it should

I have the following code:
- (void)updateServerList {
[crazyStuff reloadData];
NSLog(#"Hi");
}
- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
NSLog(#"Numbers have Changed");
return [serverBrowser.servers count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row
{
NSLog(#"Starting updates for table");
NSNetService* server = [serverBrowser.servers objectAtIndex:row];
return [server name];
}
- (IBAction)tableViewSelected:(id)sender
{
//row = [sender selectedRow];
NSLog(#"the user just clicked on row ");
}
This is part of a chat program that I'm trying to expand. It was designed for the iOS and I'm working to get it to work on my laptop and chat with my iPad. I know that updateServerList is called correctly from my log statement. I also now that numberOfRowsInTableView: is called on startup but not anytime else. I would like to have my Table (the outlet crazyStuff) updated everytime updateServerList is called. How would I trouble shoot to see if it is or is not? I am not seeing the data show up in the table that "should" be there
I take it then that at startup, when numberOfRowsInTableView is called, it returns 0? If not, is tableView:objectValueForTableColumn:row: ever called then?
My first guess would be the "crazyStuff" is, in fact, not connected to anything or connected to something other than the table view you intend. Might want to log the value of crazyStuff in updateServerList.
Within updateServerList you should [crazyStuff noteNumberOfRowsChanged]. I thought reloadData causes the table to re-ask its dataSource for the numberOfRowsInTableView:, though...