The NSButtonCell from the code bellow works fine, but it doesn't visualy display the check icon when clicked. Any ideas why?
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([[tableColumn identifier] isEqualToString:#"select"]) {
NSButtonCell *cell = [[NSButtonCell alloc]init];
[cell setButtonType:NSSwitchButton];
[cell setTarget:self];
[cell setAction:#selector(checkboxChanged:)];
[cell setTitle:#""];
return cell;
} else {
NSCell *cell = [tableColumn dataCell];
return cell;
}
}
Here is the action code:
- (IBAction)checkboxChanged:(id)sender {
NSButtonCell *aCell = [[sender tableColumnWithIdentifier:#"select"]
dataCellForRow:[sender selectedRow]];
if ([aCell state] == NSOnState) {
NSLog(#"ON");
[aCell setState:NSOffState];
} else {
NSLog(#"OFF");
[aCell setState:NSOnState];
}
}
I'm not sure why your first solution didn't work—maybe you didn't have your delegate hooked up? Note that the data source and the delegate are two separate things.
The method you implemented in your answer, which is a data source, is not the proper place to do that. The data source, and particularly numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:, should only return the content being presented, not create view/cell objects.
I recommend creating and configuring the NSButtonCell in your xib, if you have one, or in the same place where you create your table view and its columns—that's also the correct place to set each column's cell.
I solved this problem by changing the above code from the NSOutlineView delegate method outlineView: dataCellForTableColumn:item: to the delegate method outlineView: objectValueForTableColumn: byItem:
Like so:
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
if ([[tableColumn identifier] isEqualToString:#"select"]) {
NSButtonCell* cell = [tableColumn dataCell];
[cell setSelectable:YES];
[cell setEnabled:YES];
[cell setTransparent:NO];
[cell setButtonType:NSSwitchButton];
[cell setTarget:self];
[cell setAction:#selector(checkboxChanged:)];
[cell setTitle:#""];
return cell;
}
return nil;
}
And now it works perfectly.
Related
I am developing a MAC application and included the tableView.
Want to change the Colour of selected row to yellow.
Set this on your table view:
[yourtableview setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
And implement the following delegate method of NSTableView as:
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
if ([[aTableView selectedRowIndexes] containsIndex:rowIndex])
{
[aCell setBackgroundColor: [NSColor yellowColor]];
}
else
{
[aCell setBackgroundColor: [NSColor whiteColor]];
}
[aCell setDrawsBackground:YES];
}
If you want to heighlight only individual cell of column then implement like this below:-
- (void)tableView:(NSTableView *)tableView
willDisplayCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
if ([[tableColumn identifier] isEqualToString:#"yourColumm"])
{
[cell setBackgroundColor:[NSColor yelloColor]];
}
}
I'm creating an NSTableView programatically. numberOfRowsInTableView is the only method that gets called when I assign the delegate and dataSource. The NSTableView remains blank and empty. What am I doing wrong?
in myDocument.m:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
NSTableView *tableView = [NSTableView alloc] initWithFrame:CGRectMake(0,0,400,400)];
// I've tried putting the delegate before the datasource. No change.
tableView.dataSource = self;
tableView.delegate = self;
// I've tried adding the table before assigning the delegate/datasource. No change.
[aController.window.contentView addSubview:tableView];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSLog(#"making view for table...");
NSBox *box = [[NSBox alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
box.borderColor = [NSColor blackColor];
box.borderWidth = 10;
return box;
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
NSLog(#"getting data...");
return #{ #"myKey": #"myValue" };
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
NSLog(#"getting count...");
return 2;
}
numberOfRowsInTableView is the only thing that get's called. Why? How can I fix this?
You table view doesn't contain any columns and therefore doesn't call the viewForColumn: delegate method.
Replace your initialization code in windowControllerDidLoadNib with the following and your delegate methods will get called:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
NSTableView *tableView = [[NSTableView alloc] initWithFrame:CGRectMake(0,0,400,400)];
[tableView addTableColumn:[[NSTableColumn alloc] initWithIdentifier:#"MyColumn"]];
tableView.dataSource = self;
tableView.delegate = self;
[aController.window.contentView addSubview:tableView];
}
Probably you can use Interface Builder to configure & layout your tableview.
For learning purposes i would like to convert a cell-based NSOutlineView to a view-based one,
basically i would like the following:
instead of a normal cell, i'd like an 'image and text table cell view'
the image can be the stock NSApplicationIcon and the text can just be 'hello world' :)
I'd like to do this without using bindings and NSTreeController
Here is the 'worlds simplest NSOutlineView' example http://www.cocoasteam.com/Cocoa_Steam/Worlds_Simplest_Demo.html
I wonder if someone could modify it to make it view-based and work like i said above :) :)
I've tried looking at apple examples, and searching elsewhere on the internet but i still can't get it to work - so thanks very much in advance :)
I have created a little sample project which does just that.
Display a list of items
Edit the items in a master-detail fashion
Remove and add items
Usage of bindings
Check out besi/mac-quickies on github.
Most of the stuff is either done in IB or can be found in the AppDelegate
OK, so you want an NSOutlineView with ImageAndTextCell cells, right?
Let's do one of the most typical examples of this kind : a simple file explorer.
What we'll need :
an NSOutlineView (put an outline to your AppDelegate, as fileOutlineView)
create 3 columns in the Outline with the following Identifiers (set them up in Interface Builder) : NameColumn, SizeColumn, ModifiedColumn
Now, as for the rest, I'll do it all programmatically, so that you get a good idea of what's going on...
How to set it up (e.g. in - (void)awakeFromNib):
// set the Data Source and Delegate
[fileOutlineView setDataSource:(id<NSOutlineViewDataSource>)self];
[fileOutlineView setDelegate:(id<NSOutlineViewDelegate>)self];
// set the first column's cells as `ImageAndTextCell`s
ImageAndTextCell* iatc = [[ImageAndTextCell alloc] init];
[iatc setEditable:NO];
[[[fileOutlineView tableColumns] objectAtIndex:0] setDataCell:iatc];
Connecting the dots :
/*******************************************************
*
* OUTLINE-VIEW DATASOURCE
*
*******************************************************/
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if ([item isFolder])
return YES;
else
return NO;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (item==nil)
{
// Root
return [[filePath folderContentsWithPathAndBackIgnoringHidden] count];
}
else
{
if ([item isFolder])
{
return [[item folderContentsWithPathAndBackIgnoringHidden] count];
}
else
{
return 0;
}
}
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil)
{
// Root
return [[filePath folderContentsWithPathAndBackIgnoringHidden] objectAtIndex:index];
}
if ([item isFolder])
{
return [[item folderContentsWithPathAndBackIgnoringHidden] objectAtIndex:index];
}
// File
return nil;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)theColumn byItem:(id)item
{
if ([[theColumn identifier] isEqualToString:#"NameColumn"])
{
return [item lastPathComponent];
}
else if ([[theColumn identifier] isEqualToString:#"SizeColumn"])
{
if ([item isFolder]) return #"--";
else return [NSString stringWithFormat:#"%d",[item getFileSize]];
}
else if ([[theColumn identifier] isEqualToString:#"ModifiedColumn"])
{
if ([item isFolder]) return #"";
else return [NSString stringWithFormat:#"%#",[item getDateModified]];
}
// Never reaches here
return nil;
}
/*******************************************************
*
* OUTLINE-VIEW DELEGATE
*
*******************************************************/
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
return NO;
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
[cell setDrawsBackground:NO];
if ([item isFileHidden]) [cell setTextColor:[NSColor grayColor]];
else [cell setTextColor:[NSColor whiteColor]];
if ([[tableColumn identifier] isEqualToString:#"NameColumn"])
{
if ([item isFolder])
[cell setImage:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] size:15.0];
else
[cell setImage:[[NSWorkspace sharedWorkspace] iconForFile:item] size:15.0];
if ([item isFileHidden])
{
[cell setFileHidden:YES];
}
else
{
[cell setFileHidden:NO];
}
}
}
Hint : ImageAndTextCell class can be found here. You'll also notice a few other methods I'm using, which are obviously NOT supported by Cocoa (e.g. isFileHidden, isFolder or folderContentsWithPathAndBackIgnoringHidden) but it's not that difficult to implement them yourself...)
To return view to OutlineView column Instead of using datasource method that return objectValue:
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)theColumn byItem:(id)item
USE THE DATASOURCE METHOD THAT RETURN VIEW!!!!!!!!:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
everything else is the same(minimal req is the first three datasource methods, you don't need the delegate methods) but,
you can't use willdisplaycell its called only for cell based , do everything to the view in the viefortablecolumn method like this:
if ([[tableColumn identifier] isEqualToString:#"YourColumnIdentifier"]){
NSTableCellView *cell = [outlineView makeViewWithIdentifier:#"YourViewsIdentifier" owner:self];
[cell.textField setStringValue:[(YourItem *)item name]];
[cell.imageView setImage:[(YourItem *)item image]];
return cell;
}
return nil;
and don't forget to set identifiers , and to set the OutlineView to be View Based(in IB ...).
Check TableViewPlayground, also View Based NSTableView Basic to Advanced from WWDC 2011.
I have created custom cells with a button that changes the state of a row from "availible" to "bought", and I also have an UIImageview which shows the state. I want the cell to redraw with the new state when the user presses the button.
I have been able to get the UIImage to be redrawn after the User scrolls the cell out of view. This is what I have so far:
Initialization
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = YES;
self.contentSizeForViewInPopover = CGSizeMake(600.0, 560.0);
//[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.tableView setRowHeight:60];
[self.tableView setBackgroundColor:[UIColor blackColor]];
}
Got only one section:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
And all items are in the "paketListe" array:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [paketListe count];
}
The cellForRow function only gets called, when the cell is out of view and when the cell is initially drawn.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CellForRow called!");
PaketTableViewCell *cell = [[[PaketTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil] autorelease];
[cell setDelegate:self];
Paket *tempPaket = [paketListe objectAtIndex:[indexPath row]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *userDocumentsPath = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/%#/%#",userDocumentsPath, [tempPaket productID], [tempPaket previewPic]];
[cell setProductIndex:[indexPath row]];
//... Setting some Attributes in my custom cell, cut out for simplicity....
cell.backgroundColor = [UIColor clearColor];
return cell;
}
The code for the Button, this is a delegate call, so my custom sell calls [delegate buttonPushed:(int)];
- (void) buttonPushed:(int) myProductIndex {
NSLog(#"Pushed: %#", [[paketListe objectAtIndex:myProductIndex] productID]);
CoreDataWrapper *tempCDW = [[CoreDataWrapper alloc] init];
[tempCDW initCoreData];
Paket *tempPaket = [paketListe objectAtIndex:myProductIndex];
//Doing some work in Core Data an my "paketListe" array. Left out for simplicity...
[tempCDW release];
//Here it begins....
[[self tableView] reloadData];
[[self tableView] beginUpdates];
[[self tableView] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:myProductIndex inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}
Now I tried the reloading with different commands... Reload the whole thing with just reloadData or the begin-/endUpdate thing. Nothing refreshes the cell. After several hours of reading I think I did everything right, but no refresh.
I appreciate any help.
Quick and dirty:
- (void)configureCell:(PaketTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
//Any cell-specific code
[cell setNeedsLayout]; //or [cell setNeedsDisplay];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Code to create any/a general cell
[self configureCell:cell];
return cell;
}
- (void) buttonPushed:(int) myProductIndex {
//Code to identify the cell in which your button is (use "super")
[self configureCell:cellForPushedButton];
}
I got to know your problem you have missed in reloadRowsAtIndexPaths "nil".
Do as i wrote below, it will work.
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:myProductIndex inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
Does anyone here know how to make cells in NSOutlineView's editible? Im using the sampe code from apple and I cant seem to get it work at all.
I am trying to set it up so that when you click twice in rapid succession on a cell in the NSOutlineView, the cell becomes editible so the user can update the text inside the cell. (In the same way as it works in xcode, and mail and so on).
I am including most of the rest of the code of this controller in the vain hope someone can spot what I am doing wrong, this is very frustrating. I know shouldEditTableColumn is being called as it is returning the NSLog message upon double click.
#implementation DisplayHierarchyController
- (void)awakeFromNib {
// cache the reused icon images
folderImage = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain];
[folderImage setSize:NSMakeSize(16,16)];
objectImage = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericPreferencesIcon)] retain];
[objectImage setSize:NSMakeSize(16,16)];
diagramImage = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericEditionFileIcon)] retain];
[diagramImage setSize:NSMakeSize(16,16)];
//
// Tell the outline view to use a special type of cell
//NSTableColumn *tableColumn = [[outline tableColumns] objectAtIndex: 0];
//ImageTextCell *imageTextCell = [[[ImageTextCell alloc] init] autorelease];
//[imageTextCell setEditable:YES];
//[tableColumn setDataCell:imageTextCell];
//
[[[outline tableColumns] objectAtIndex: 0] setEditable: YES];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSLog(#"edit %#", tableColumn);
return YES;
}
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
ImageTextCell *imageTextCell = [[[ImageTextCell alloc] init] autorelease];
[imageTextCell setEditable:YES];
return imageTextCell;
}
// Returns the object that will be displayed in the tree
- (id)outlineView: (NSOutlineView *)outlineView child: (int)index ofItem: (id)item {
if(item == nil)
return [[document children] objectAtIndex: index];
if([item isKindOfClass: [Item class]])
return [[item children] objectAtIndex: index];
return document;
}
- (BOOL)outlineView: (NSOutlineView *)outlineView isItemExpandable: (id)item {
if([item isKindOfClass: [Item class]])
return [[item children] count]>0;
return NO;
}
- (int)outlineView: (NSOutlineView *)outlineView numberOfChildrenOfItem: (id)item {
if(item == nil)
return document.children.count;
if([item isKindOfClass: [Item class]])
return [[item children] count];
return 0;
}
- (id)outlineView: (NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
if([item isKindOfClass: [Item class]])
return [item name];
return #"n/a";
}
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
NSLog(#"setObjectValue called");
}
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
[cell setEditable: YES];
[cell setAllowsEditingTextAttributes: YES];
[(ImageTextCell*)cell setImage: objectImage];
}
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor {
return YES;
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
if ([[fieldEditor string] length] == 0) {
// don't allow empty node names
return NO;
} else {
return YES;
}
}
#end
I know this is a very old post, but if any one is experiencing the same issue, this may not be an issue related to code. For my case it was an issue related to do with a value set in the XIB itself.
So lets say you've copied all the Apple code, and you've got your NSOutlineView up and running, and some how its still not editable, go to your XIB and set the following setting of the NSTextField of the cell you want to be editable. In my case the behavior setting was set to none by default. Maybe its the same problem for you
Cheers.
Is the column itself set as editable? Ordinarily, you would do this in IB.
Also, have you implemented the outlineView:setObjectValue: method in your data source?
Ive just discovered I can "fake it" by altering the shouldEditTableColumn. Its really not ideal, but it works. After so many hours trying to get it to work, at least this is something:
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSLog(#"edit %#", tableColumn);
[outline editColumn:0 row:[outline selectedRow] withEvent:[NSApp currentEvent] select:YES];
return YES;
}
I found a way around this. Set the data cell for the column in IB (programmatically in awakeFromNib should work too). I actually use 2 different custom cell classes. My solution:
NSCell *cell = [tableColumn dataCellForRow: [outlineView rowForItem: item]];
if ([item isKindOfClass: [NSString class]])
return [[[ShadowTextCell alloc] initTextCell: [cell stringValue]] autorelease];
return cell;