Implement checkbox in NSTableView in mac application - xcode

First of all, I searched for 3 days and have not found what I am looking for.
Second point, I am VERY new to Xcode programming so PLEASE keep your answer as simple and/or detailed as possible!
Inside my NSTableView I need the first column to be text followed by X number of checkbox columns with the last column to be text. The X number of columns is a variable based on the number of entries I read from a SQLite file.
My questions are:
How do I define at runtime what type and how many columns I have?
How do I know whether a checkbox value is checked or not?
How do I add rows to the tableview if I don't know the number of cells it has?
Thanks for taking the time to answer!
Best regards,
Igor

You want to create a table at runtime, and you dont know how many columns will be there!!!
Thats a big deal.
Read NSTableView documentation and you will find addTableColumn: method. in the loop go on to create colmn and dont forget to give identifier to all columns.
And over this you want to have a checkbox. Creating checkbox is not difficult either.
EDIT:
For checkbox implementation, find a project here.
Draw a checkBoxCell in the column you want to have checkbox. Or you can do it programatically.
I did it through IB.
You should create an array that will store the states for each(number of rows) checkboxes.
- (id)init {
self = [super init];
if (self) {
states=[[NSMutableArray alloc] initWithObjects:#"1", #"0", #"1", nil];
}
return self; }
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"Num of rows---- %ld", [names count]);
return [names count];
}
Check for tableIdentifier having value "check".
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if ([[tableColumn identifier] isEqualTo:#"name"]) {
return [names objectAtIndex:row];
}
else if ([[tableColumn identifier] isEqualTo:#"check"]) {
return [states objectAtIndex:row];
}
else if ([[tableColumn identifier] isEqualTo:#"states"]) {
return [states objectAtIndex:row];
}
return 0;
}
Update the value of checkbox as per on/off.
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)value forTableColumn:(NSTableColumn *)column row:(NSInteger)row {
[states replaceObjectAtIndex:row withObject:value];
[tableView reloadData];
}

Related

Adding elements with content not working in NStable view in cocoa

I created a tableview and added image & text table view cell designed it now i thought to add another label in the row and assigned some values to the label based on the rows but it is not working and one more problem i cant hide label when the label in cell while in view i can able to hide
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSImage *profile=[NSImage imageNamed:#"header_menubg.png"];
NSString *identifer=[tableColumn identifier];
if([identifer isEqualToString:#"Mainidentifier"])
{
cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
[cellView.textField setStringValue:[_homearray objectAtIndex:row]];
[cellView.imageView setImage:profile];
return cellView;
}
if(row==0)
{
_countLabel.stringValue=#"40";
[cellView.textField setHidden:YES];
}
return nil;
}
And even i tried to hide text field in some rows that too not working what may be the problem

Using a different view in editing mode in a view based NSTableView

I have a NSTableView with a single NSTableCellView column that let's say, has an icon, name and an optional date.
When you edit a row, I want to replace the whole view with a simple NSTextField, and I will do some parsing to that text and extract that optional date, if present.
My question is, how would you implement this editing mechanism?
I tried returning a different view in the tableView:viewForTableColumn:row, something like:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
BOOL isSelected = [tableView isRowSelected:row];
if (isSelected)
{
NSView *view = [tableView makeViewWithIdentifier:#"editor" owner:self];
....snip....
return view;
}
else
{
TaskView *view = [tableView makeViewWithIdentifier:#"view" owner:self];
....snip....
return view;
}
}
and then whenever the selected row changes, trying to request a refresh on that row.
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
NSTableView *table = [aNotification object];
NSUInteger rowIndex = [table selectedRow];
[table reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex]
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
}
It doesn't quite work, and the code feels a bit dirty.
It must be a better way of doing this, and I can't seem to find in the docs or online.

NSButtonCell of Check type within NSTableView does not allow to have value changed

I have window with 3 table views (10.7.2, Xcode 4.2).
They are all created in IB and NSButtonCells are connected with outlets.
I created controller class and I filled all three views with some sample data:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
return 10;
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSButtonCell *buttonCell;
if(aTableView == dimensionTable) {
[dimensionButtonCell setTitle:#"Dimension"];
buttonCell = dimensionButtonCell;
}
else if(aTableView == shopTable) {
[shopButtonCell setTitle:#"Shop"];
buttonCell = shopButtonCell;
}
else if(aTableView == countryTable) {
[countryButtonCell setTitle:#"Country"];
buttonCell = countryButtonCell;
}
return buttonCell;
}
I have 2 questions:
I cannot change checkbox state through GUI. I can change it programatically, though. It blinks a bit, when you hold down mouse button, but doesn't allow change...
I tried to fill data as with views, without outlets to cells. It didn't work. Are NSButtonCell cels within cell views somehow different as view based Table Views or "normal" cel based Table Views?
After long struggle I manage to find the solution for the problem. One part of the problem was simple bug at the data model side, but it wasn't crucial, something much more difficult was to be done with NSTableView delegate and datasource.
THere were mainly 3 difficulties that prevented good understanding and managing this problem:
apple's documentation lacks of any reasonable explanation about differences and typical usage of - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndexin table view's data source and - (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row of its delegate. While it may seem that you would need latter method, because NSButtonCells are custom NSCells it turns out it is not necessary, but I left it at the end anyway.
internal conversions in NSTableView methods
problem is not documented almost anywhere on the net
Here are steps you should do:
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
buttonCell = [aTableColumn dataCell];
NSString *columnKey = [aTableColumn identifier];
return buttonCell;
}
You can see this method has to be implemented whether you use it or not.
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
buttonCell = [tableColumn dataCell];
NSString *columnKey = [tableColumn identifier];
if(tableView == dimensionTable) {
// returnObject = #"Dimension";
// [dimensionButtonCell setTitle:#"Dimension"];
// buttonCell = dimensionButtonCell;
}
else if(tableView == shopTable) {
[buttonCell setState:[[mySelectedShops objectAtIndex:row] integerValue]];
[buttonCell setTitle:[myShops objectAtIndex:row]];
}
else if(tableView == countryTable) {
[buttonCell setState:[[mySelectedCountries objectAtIndex:row] integerValue]];
[buttonCell setTitle:[myCountries objectAtIndex:row]];
}
return buttonCell;
}
you can see I used second method, however objectValueForTableColumn could be used solely.
You can also see, I have NSMutableArray mySelectedShops and mySelectedCountries to hold NSInteger (1 or 0) wrapped in NSNumber for each row in Table View.
If you set the state or integerValue of NSCell makes no difference. Both will check and uncheck NSButtonCell with values 1 or 0 of NSInteger type.
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSString *columnKey = [aTableColumn identifier];
if(aTableView == dimensionTable) {
// [dimensionButtonCell setTitle:#"Dimension"];
// buttonCell = dimensionButtonCell;
}
else if(aTableView == shopTable) {
[mySelectedShops replaceObjectAtIndex:rowIndex withObject:[NSNumber numberWithInteger:[(NSCell*)anObject integerValue]]];
}
else if(aTableView == countryTable) {
[mySelectedCountries replaceObjectAtIndex:rowIndex withObject:[NSNumber numberWithInteger:[(NSCell*)anObject integerValue]]];
}
}
Although I passed NSInteger value to NSCell object, anObject here is of __NSCFBoolean type, which means something doesn't work as expected. To be able to replace object value to arrays I have casted it to NSCell only to get integerValues. It actually works without cast as well, so it is another mystery to me, but I like it more that way.
It is clear Apple is moving to view based cells like in UITableView. Still, I hope this will help to somebody.

CheckBox in tableview

I am facing trouble in putting check boxes into a UITableView. I am posting a part of my code here.
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSButtonCell *cell=[[NSButtonCell alloc] init];
NSString *strDisplayPlaylistName;
strDisplayPlaylistName=[playListNameArray objectAtIndex:row];
[cell setTitle:strDisplayPlaylistName];
[cell setAllowsMixedState:YES];
[cell setButtonType:NSSwitchButton];
return cell;
}
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSCell *aCell = [aTableColumn dataCellForRow:rowIndex];
[aCell setNextState];
//NSCell *aCell=[aAddedCells objectAtIndex:rowIndex];
//[aCell setNextState];
}
I got the checkboxes inside the UITableView. But the problem is that I can't uncheck the buttons. Is there anything more to do. I am new to cocoa programming.
You're missing a couple of important pieces. You need to update your model (data stcuture) in response to the tableValue:setObjectValue:forTableColumn:row: message, so that you can correctly return the new value from tableView:objectValueForTableColumn:row: method.
Here are some table data source methods assuming you have a 'myRows' array filled with objects with a 'booleanAttribute' property.
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [myRows count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
BOOL value = [[myRows objectAtIndex:row] booleanAttribute];
return [NSNumber numberWithInteger:(value ? NSOnState : NSOffState)];
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)value forTableColumn:(NSTableColumn *)column row:(NSInteger)row {
[[myRows objectAtIndex:row] setBooleanAttribute:[value booleanValue]];
}
You should also setup your table cell in interface builder. You can drag a button cell configured like a standard check box directly onto one of your table columns.
I'm not sure why you're creating the cell in code. You can just drag the cell onto the table column in Interface Builder.
Also, setObjectValue: is where you respond to the change in the cell's state. The user has already changed the cell's state to off; then you send setNextState and change it back. That's why the cell doesn't appear to uncheck: you keep re-checking it.
What you need to do is not touch the cell at all, but set the object value (which, for this column, will probably be a Boolean NSNumber containing either YES or NO) as the new value of the appropriate property in your model.
Also, of course, make sure the column is set as editable.
If you have your NSTableView content mode set to "Cell Based" it will be "View Based" when you move the checkbox over.

outlineView:dataCellForTableColumn:item: has strange side effect

I have an outline view delegate and am overriding outlineView:dataCellForTableColumn:item: to make the cells in my outline view into buttons (see this question). Here is the code form my delegate:
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item;
{
MyCell * myCell = [[MyCell alloc] init];
// return nil;
return myCell;
}
Doing this has a strange side effect. In my outline view's data source, the method outlineView:objectValueForTableColumn:byItem: always gets a null value for tableColumn.
The code is:
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
printf("tableColumn:%s\ttable identifier: %s\n", [[tableColumn className] cString], [[tableColumn identifier] cString]);
return [item valueForKey:[tableColumn identifier]];
}
And the output is:
tableColumn:(null) table identifier: (null)
What's strange is that this only happens when I implement the outlineView:dataCellForTableColumn:item: method. What am I missing here?
EDIT:
Modifying the delegate function like this seems to fix the problem:
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item;
{
printf("delegate column identifier: %s\n", [[tableColumn identifier] cStringUsingEncoding:NSASCIIStringEncoding]);
if (tableColumn == nil)
{
return nil;
}
MyCell * aCustomCell = [[MyCell alloc] init];
return aCustomCell;
}
However, I don't really understand what's going on here. If anyone can explain, that would be helpful. Thanks!
NSOutlineView has the ability for you to use a single cell to draw an entire row, rather than a separate cell for each column. It first asks for a cell passing in a nil table column. If you return a cell for that call, it uses it to draw the whole row, otherwise it continues and asks for a separate cell for each column. So, as you discovered, the solution is to return a nil cell when passed a nil table column, so things will draw normally.

Resources