Changing view for selected in view-based NSTableView - objective-c

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..

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];
}

How to know whether NSTableView is in editing?

I want to check if my NSTableView is in editing or not.
I tried to use tableView: shouldEditTableColumn: row: and tableView: setObjectValue: forTableColumn: row: functions. For example:
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
isRenaming = YES;
return YES;
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
isRenaming = NO;
...
}
However, tableView: shouldEditTableColumn: row: function was called even when I didn't try to edit the tableview.
So, sometimes, isRenaming is remained to YES.
How to know whether NSTableView is in editing?
Set your view controller to be a delegate for the text view in your table view cell.
Then you can set "isRenaming = YES" when the user triggers the [control: textShouldBeginEditing:] protocol method. You can also set "isRenaming = NO" when the user is done editing (or when they click the "done" or "save changes" button in your UI).
Finally, I found the missing case.
"tableView: shouldEditTableColumn: row:" function was called when the double-click event was occured on NSTableView.
So,
- (void)tableViewDoubleClicked:(id)sender {
isRenamed = NO;
...
}
It solved the problem.
Why not just check edited​Row?
let isBeingEdited = yourTable.edited​Row != -1
This property does not apply to view-based table views. In a view-based table view, the views are responsible for their own editing behavior. For other tables, the value reflects the index of the row being edited or –1 when there is no editing session in progress.

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.

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.

Coloring NSTableView Text per row

I have a NSTableView that is displaying an array of objects I have. For each of these objects (rows) I would like to change the color of the text displayed depending on the results of a function I run on each object;
So for example all the object in the table that exist in another list (or some other requirement) I want to display them in green text, and objects that don't exist display in red.
How would I go about doing this?
Assuming that you have NSTextFieldCell in your table (for other cells, setting text color may vary), you can achieve this by implementing a NSTableView's delegate method.
First, you have to define a delegate for the NSTableView, either in Interface Builder or in your code. This can be your application controller for example.
Then, just implement the following method:
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSTextFieldCell *cell = aCell;
if (...) {
[cell setTextColor:[NSColor greenColor]];
} else if (...) {
[cell setTextColor:[NSColor redColor]];
} else {
[cell setTextColor:[NSColor blackColor]];
}
}
Each time the NSTableView will draw a cell, you have the opportunity to modify it before it get drawn.
Check out the NSTableViewDelegate documentation page for more details.