I'm trying to create a homework planner app that has two types of TableCellViews in a View Based NSTableView. One type is a narrow bar that just has a label of what subject the below homework is for, and the other type is a row to input homework items. (I'll include a screenshot below.)
My question is: when creating new rows in a TableView, how do you specify which type of row you'd like to create? I'm assuming it has something to do with identifiers, but I can't find any information on how to use them in this way.
This is basically how it would look:
You are on the right track with the identifiers. Here's how you use them.
First setup your NSTableView with your specific row types (as you've probably already done). In the screenshot below I made one row with a title and description and another with a few buttons.
Next, you need to setup the desired identifiers. Click the first row in Interface Builder and select the Identity Inspector. Pick a unique identifier for your first row. Do the same for the other(s).
Finally, in your implementation create a new row of a specific type using the following code:
TableViewController.m
#pragma mark - NSTableViewDelegate
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
NSTableCellView *cell;
if(someCondition == YES) {
cell = [self.tableView makeViewWithIdentifier:#"ButtonRow" owner:self];
} else {
cell = [self.tableView makeViewWithIdentifier:#"TitleDescriptionRow" owner:self];
}
return cell;
}
If you're looking for a more in depth tutorial, check out Cocoa Programming L51 - View-Based NSTableView (YouTube video, not by me).
Related
When I use a custom view as the cell of a view-based NSTableView, the custom view is somewhat below the table row. When I click on it, instead of affecting the elements (e.g. text field) custom view, the table row was selected (and highlighted). I have to reclick to select the text field.
- (NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"We are creating views!");
NSTableCellView *newView;
newView = [tableView makeViewWithIdentifier:#"PostCell" owner:self];
NSTextField *newTextField = [[NSTextField alloc] init];
[newView addSubview:newTextField];
return newView;
}
When I disable the row selection according to NSTableView - Disable Row Selection, there was no selection.
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)tableView {
return NO;
}
But I still cannot select directly the text field. What's worse, I cannot even select it using the mouse. Only tab on the keyboard works.
There seem to be something above it. But is it the "table column" shown in interface builder? Or something else?
How can I fix this?
Use a custom subclass of NSTableView and override -validateProposedFirstResponder:forEvent: to return YES.
See this blog entry from the Apple engineer who wrote the view-based table view code.
Make sure following code is present.
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex {
return YES;
}
You may try logging the subviews Or you can check superviews of view.
This will help to understand view hierarchy.
Also on side note if one of the view's userInteraction is disable then it's subview's won't be able to receive the events. Please verify that all the views and it's subviews userInteraction is enable.
I hope this helps.
I have been trying unsuccessfully to use so-called "Header Cells" in my view-based (which I'd like to keep that way) NSOutlineView without success.
I have tried the following things:
I have successfully set up -(BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item for the object I want to make headers.
I have thoroughly tested my delegate and data-source, so we can assume they are set up properly.
When it comes to -(NSView *)outlineView:(NSOutlineView *)ov viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item definition, I am stuck.
I had the feeling from my readings that there are things to set up in IB, but I can't figure out what. What is this #"HeaderCell" referring to?
I tried this kind of stuff which are not successful at all (displays an empty-view)
-(NSView *)outlineView:(NSOutlineView *)ov viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{ NSTableCellView *result = nil;
result = [ov makeViewWithIdentifier:#"HeaderCell" owner:self];
[[result textField]setStringValue:#"myString"];
return result;
}
What am I missing ?
Source : View-based NSOutlineView header cell font issues
The #"HeaderCell" refers to the Table Cell View identifier. In IB select the Table Cell View representing the Header Cell and on the utility sidebar select identity inspector...you should see the identifier field.
NB : From Cory's Answer
Here's a graphical explanation of how to set it up in Interface Builder :
In IB select the Table Cell View representing the Header Cell and on the utility sidebar select identity inspector...you should see the identifier field
I'm new at developing with XCode and Objective-C and I hope you can help me.
The problem is, I have an UITableViewController with an UITableView (created with the InterfaceBuilder).
The cells under the section headers are expandable.
Now I want to dynamically create multiple UITableViews under the existing TableView.
The style will be the same like the existing TableView's style.
Could you tell me how it is possible to create these TableViews programmatically?
Thank you very much
Michael
From what you are saying try using a grouped table view. Check out this link for a quick overview, and go to the grouped table view section.
Edit found this example here:
Seems like it is what you are looking for. And a very cool idea also.
You'll have to just make your own custom header row and just put that as the first row of each section. Subclassing the UITableView or the headers that are on there now would probably be a huge pain and I'm not sure you can easily get actions out of them the way they work now. You could easily set up a cell to LOOK like a header, and setup the tableView:didSelectRowAtIndexPath to expand or collapse the section it is within manually.
If I were you I'd store an array of booleans corresponding the the "expended" value of each of your sections. You could then have the tableView:didSelectRowAtIndexPath on each of your custom header rows toggle this value and then reload that specific section.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
///it's the first row of any section so it would be your custom section header
///put in your code to toggle your boolean value here
mybooleans[indexPath.section] = !mybooleans[indexPath.section];
///reload this section
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
You'd then setup your number numberOfRowsInSection to check the mybooleans value and return either 1 if the section isn't expanded, or 1+ the number of items in the section if it is expanded.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (mybooleans[section]) {
///we want the number of people plus the header cell
return [self numberOfPeopleInGroup:section] + 1;
} else {
///we just want the header cell
return 1;
}
}
You would also have to update your cellForRowAtIndexPath to return a custom header cell for the first row in any section.
How I can store text field data in a view controller into a tableView cell in a TableViewController With Xcode using data source?
That means when the user taps "+" it will show another view that has the text field. When the user enters the text and presses save, the entered data will be stored as a table cell.
First make sure you understand data sources. Then you have to implement
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return self.data.count;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
NSTableCellView * result = [tableView makeViewWithIdentifier:#"identifier" owner:self];
result.textField = #"Your special string";
return result;
}
After having setup it in IB like this and having connected the outlet of the Table View data source to your custom objects class (the class the above code is in) it should look like this
Please notice I used the same identifier as in the code so that I can get the created table cell view back easily.
self.data could be an array for example in which you store all your underlying objects (for the cell creation).
Of course you could also add any kind of UI elements to the cell view as well. In this case I use a custom subclass for the cell view. You would have to do something like this then (and set your class as the class of the cell view within IB of course. This is the part in the screenshot that has an NSTableCellView currently in it. It had to be MyGreatCellView from now on):
#interface MyGreatCellView : NSTableCellView {
IBOutlet NSTextField *files;
}
#property (assign) NSTextField *files;
Then you could also refer to result.files in the tableView:vieForTableColumn:row for example.
If something is unclear, just ask.
Getting the UITextField data onto a TableView Cell.
Here is a query for some one for the same issue.I think it will help you.
I'm currently working in a project with a NSOutlineView...
I use, of course, NSCell(s) and I need to let the ability to select text inside the cell...
Or at least... prevent the selection (and highlight) of the cells...
I search all options on IB, but can't found the right one...
Is there a way, programmatically or not, to prevent selection/highlighting of cell, nor let user select cell content ?
Thanks =)
That's not much NSCell related, maybe you're looking to implementing outlineView:shouldSelectItem: in your delegate.
On the NSCell, setEnabled:NO, may help too. From the documentation:
setEnabled:(BOOL)flag
The text of disabled cells is changed to gray. If a cell is disabled, it cannot be highlighted, does not support mouse tracking (and thus cannot participate in target/action functionality), and cannot be edited. However, you can still alter many attributes of a disabled cell programmatically. (The setState: method, for instance, still works.)
Try setting:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
You could also try overriding highlightSelectionInClipRect:, but I'm not totally sure this will work.
Let's take a quick example like the outline view below. There are 3 columns: firstName, lastName, and fullName.
In this particular example, let's say we want to only allow firstName and lastName to be editable while fullName (which is potentially derived from firstName and lastName) is not. You could set this up in Interface Builder by checking or unchecking the editable checkbox for the table column. To do that, click 3 times on one of the table columns (not the header, but inside the outline view); this first selects the NSScrollView, then the NSOutlineView, then an NSTableColumn:
You'd set the attributes like the following:
That provides a start by setting a default editable value for the entire column. If you need more control over whether a particular row's item value should be editable or not, you can use the outlineView:shouldEditTableColumn:item: delegate method:
#pragma mark -
#pragma mark <NSOutlineViewDelegate>
- (BOOL)outlineView:(NSOutlineView *)anOutlineView
shouldEditTableColumn:(NSTableColumn *)tableColumn
item:(id)item {
if ([[tableColumn identifier] isEqualToString:#"firstName"] ||
[[tableColumn identifier] isEqualToString:#"lastName"]) {
return YES;
} else if ([[tableColumn identifier] isEqualToString:#"fullName"]) {
return NO;
}
return YES;
}
If you want to control whether a particular row in the outline view is selectable (for example, you could prevent selection of a group item), you can use outlineView:shouldSelectItem:.
- (BOOL)outlineView:(NSOutlineView *)anOutlineView shouldSelectItem:(id)item {
// if self knows whether it should be selected
// call its fictional isItemSelectable:method:
if ([self isItemSelectable:item]) {
return YES;
}
/* if the item itself knows know whether it should be selectable
call the item's fictional isSelectable method. Here we
are assuming that all items are of a fictional
MDModelItem class and we cast `item` to (MDModelItem *)
to prevent compiler warning */
if ([(MDModelItem *)item isSelectable]) {
return YES;
}
return NO;
}