Change color of cell in Table View (I know it's been discussed before)

To change a single cell’s color, I have the following:

I know it is probably not the BEST way to go about it, but I do the following. When i get to a line in my code that i change a cell AND i want to call attention to it, i set the following:


set ChangeTheColorOfMailingLogCells to 1
set ChangeTheColorForOrderID to "999-999-999"
set the contents of data cell "table_status" of myRow to trackinginfo

here is the will change cell handler:


on will display cell theObject cell theCell row theRow table column tableColumn
if ChangeTheColorOfMailingLogCells  is equal to 1 then
		if the name of theObject is equal to "mailinglog_table" then
			if the identifier of tableColumn is equal to "table_status" then
				set EndiciaDetailDataSource to the data source of theObject
				
				set rowcounter to 1
				repeat with i in every data row of EndiciaDetailDataSource
					if rowcounter is equal to theRow then
						if contents of data cell "table_ordernumber" of i is equal to ChangeTheColorForOrderID then
							log "Would highlight row # " & rowcounter & " or " & theRow & " having the email: " & contents of data cell "table_customeremail" of i
							set text color of theCell to {30000, 0, 0}
							set ChangeTheColorOfMailingLogCells to 0
							set ChangeTheColorForOrderID to ""
							-- works without the following
							-- update theObject
							exit repeat
						end if
						
					end if
					set rowcounter to rowcounter + 1
				end repeat
				
				
			end if
			
		end if
	end if
end will display cell

Okay, what happens is the following:

If the row to change is the 3rd row, then the 3rd through the last rows (that column only) turn red. IF I SCROLL the table view or in any way cause it to update (by resizeing or such) then the ENTIRE column for all the rows (even the first through the second) update to red.

Anyone know why?

TIA

Jann

(edited for misspelling of will display handler)

try the following:

in the “if” part you set a color.
make an Else part, where you set the color to black (or whatever you prefer)

Have you actually been able to get either text or the data cell to have a specified color? It seems to be one of the great secrets of applescript. I’ve found all kinds of people asking the question but have never found an answer.

Jann,

many thanks for this - I didn’t know this is possible from AppleScript (I always used to subclass NSTableColumn for this + override ‘dataCellForRow:’). ‘on will display cell’ is a much easier solution :slight_smile:

@tdog:

here an example to set text and background colors in a table view (single column with an NSTextFieldCell, ‘Draw background’ option checked):

global ds

on awake from nib theObject
	set content of theObject to {"red background", "green text", "blue text", "red text", "green background", "blue background", "nothing", "blue text", "red background"}
	set ds to data source of theObject
end awake from nib

on will display cell theObject cell theCell row theRow table column tableColumn
	set cellcontents to (contents of data cell 1 of data row theRow of ds)
	if cellcontents contains "text" then
		set textCol to (my getColor(cellcontents))
		if textCol is not {} then
			set text color of theCell to textCol
			set background color of theCell to {65535, 65535, 65535}
		end if
	else if cellcontents contains "background" then
		set bCol to (my getColor(cellcontents))
		if bCol is not {} then
			set background color of theCell to bCol
			set text color of theCell to {65535, 65535, 65535}
		end if
	else
		set text color of theCell to {0, 0, 0}
		set background color of theCell to {65535, 65535, 65535}
	end if
end will display cell

on getColor(cellcontents)
	if cellcontents contains "red" then
		return {65535, 0, 0}
	else if cellcontents contains "green" then
		return {0, 65535, 0}
	else if cellcontents contains "blue" then
		return {0, 0, 65535}
	else
		return {}
	end if
end getColor

Here a screenshot of the result

D.

OK, this is great. But It does slow down operation quite a bit if you have a routine to check the contents for validity and color accordingly.

In fact, for some reason, when I double click in a cell in order to edit, the entire column turns the color of the last text colored cell. As does the cell itself if I just select it. Any ideas what might be going on?

tdog,

what’s going on? The problem with this method of colorizing the table is, that you really have to tell every cell of the colored column which colors you want - If you don’t, they’ll get the colors of the last colors specified for this column’s cell … Why? That’s because there isn’t a permanently existing cell object for every row of a column - the column creates them ‘on the fly’ - using instances of a ‘master cell’. The only chance to influence this process is the applescript handler - as described here - or an Objective-C method (subclass overriding ‘dataCellForRow:’). Both methods are called by the column whenever it is about to display a cell. The column says: ‘Hey - I want to display the cell object of row n’ and here is your chance to say - ‘Ok - take it, but change it’s textcolor or background color etc.’

If the Applescript way is too slow it’s since your script part that determines the colors has to be called everytime the column updates (which can be quite often).
So what remains might be the NSTableColumn subclass. I’ve rewritten my example of two msgs above using this method. The big advantage is speed - the subclass holds a list (NSMutableArray) for every row’s colors so if there are changes the applescript is only called once to determine the colors of a cell.

  1. The subclass files:
  • go to Interface Builder → MainMenu.nib → Classes Tab and Subclass ‘NSTableColumn’ (Ctrl+Click it and select the first menu item of the context menu)

  • name this Subclass -(‘ColoredCellsTableColumn’ in my example)

  • select the new Subclass and Controll-Click it - then select ‘Create Files for …’

  • select the column you want to use the subclass for and select the ‘Custom Class’ Tab of the Inspector Window (or enter Apple+5)

  • choose the new Subclass for the column

  • save

  • then switch to Xcode

  • there should be 2 new files added to your project: .h and .m

copy & paste this:

//File: ColoredCellsTableColumn.h:

#import <Cocoa/Cocoa.h>

@interface ColoredCellsTableColumn : NSTableColumn{
	NSMutableArray *cellColors;
	NSMutableArray *textColors;
}

-(void)initColors;
-(void)setColor:(NSArray *)theColor forRow:(int)row;
-(void)setTextColor:(NSArray *)theColor forRow:(int)row;
-(void)insertRow:(int)row;
-(void)insertRow:(int)row withBackgroundColor:(NSArray *)bColor withTextColor:(NSArray *)tColor;
-(void)deleteRow:(int)row;
@end
//File: ColoredCellsTableColumn.m:

#import "ColoredCellsTableColumn.h"

@implementation ColoredCellsTableColumn

-(void) initColors{
	cellColors = [[NSMutableArray alloc] init];
	textColors = [[NSMutableArray alloc] init];
}

- (void) dealloc {
	[cellColors release];
	[textColors release];
	[super dealloc];
}

- (id)dataCellForRow:(int)row{
	NSTextFieldCell *returnedCell = [super dataCellForRow:row];
	if ( [cellColors objectAtIndex:row] ) {
		[returnedCell setBackgroundColor:[cellColors objectAtIndex:row]];
	} else {
   	   [returnedCell setBackgroundColor:[NSColor whiteColor]];
	}
	if ( [textColors objectAtIndex:row]) {
		[returnedCell setTextColor:[textColors objectAtIndex:row]];
	} else {
   	   [returnedCell setTextColor:[NSColor blackColor]];
	}
   return returnedCell;
}

-(void)setColor:(NSArray *)theColor forRow:(int)row{
	row--;
	int arrayCount = [cellColors count];
	int n;
	if (arrayCount < row) {
		for(n=arrayCount; n<row; n++) {
			[cellColors addObject:[NSColor whiteColor]];
		} 
		[cellColors addObject];
	} else if (arrayCount == row) {
		[cellColors addObject:
			[NSColor colorWithCalibratedRed:[[theColor objectAtIndex:0] intValue] / 65535.0 
									  green:[[theColor objectAtIndex:1] intValue] / 65535.0 
									   blue:[[theColor objectAtIndex:2] intValue] / 65535.0 
									  alpha:1.0]];
	} else {
		[cellColors replaceObjectAtIndex:row withObject:
			[NSColor colorWithCalibratedRed:[[theColor objectAtIndex:0] intValue] / 65535.0 
									  green:[[theColor objectAtIndex:1] intValue] / 65535.0 
									   blue:[[theColor objectAtIndex:2] intValue] / 65535.0 
									  alpha:1.0]];
	}
}

-(void)setTextColor:(NSArray *)theColor forRow:(int)row{
	row--;
	int arrayCount = [textColors count];
	int n;
	if (arrayCount < row) {
		for (n=arrayCount; n<row; n++) {
			[textColors addObject:[NSColor blackColor]];
		} 
		[textColors addObject];
	} else if (arrayCount == row) {
		[textColors addObject:
			[NSColor colorWithCalibratedRed:[[theColor objectAtIndex:0] intValue] / 65535.0 
									  green:[[theColor objectAtIndex:1] intValue] / 65535.0 
									   blue:[[theColor objectAtIndex:2] intValue] / 65535.0 
									  alpha:1.0]];		
	} else {
		[textColors replaceObjectAtIndex:row withObject:
			[NSColor colorWithCalibratedRed:[[theColor objectAtIndex:0] intValue] / 65535.0 
									  green:[[theColor objectAtIndex:1] intValue] / 65535.0 
									   blue:[[theColor objectAtIndex:2] intValue] / 65535.0 
									  alpha:1.0]];
	}
}


-(void)insertRow:(int)row{
	row--;
	if (row < [textColors count]) {
		[textColors insertObject:[NSColor blackColor] atIndex:row];
	} else {
		[textColors addObject:[NSColor blackColor]];
	}
	if (row < [cellColors count]) {
		[cellColors insertObject:[NSColor whiteColor] atIndex:row];
	} else {
		[cellColors addObject:[NSColor blackColor]];
	}
}

-(void)insertRow:(int)row withBackgroundColor:(NSArray *)bColor withTextColor:(NSArray *)tColor{
	row--;
	if (row < [textColors count]) {
		[cellColors insertObject:
		[NSColor colorWithCalibratedRed:[[bColor objectAtIndex:0] intValue] / 65535.0 
								  green:[[bColor objectAtIndex:1] intValue] / 65535.0 
								   blue:[[bColor objectAtIndex:2] intValue] / 65535.0 
								  alpha:1.0]
					 atIndex:row];
	} else {
		[cellColors addObject:
		[NSColor colorWithCalibratedRed:[[bColor objectAtIndex:0] intValue] / 65535.0 
								  green:[[bColor objectAtIndex:1] intValue] / 65535.0 
								   blue:[[bColor objectAtIndex:2] intValue] / 65535.0 
								  alpha:1.0]
					];
	}
					
	if (row < [cellColors count]) {	
		[textColors insertObject:	
		[NSColor colorWithCalibratedRed:[[tColor objectAtIndex:0] intValue] / 65535.0 
								  green:[[tColor objectAtIndex:1] intValue] / 65535.0 
								   blue:[[tColor objectAtIndex:2] intValue] / 65535.0 
								  alpha:1.0]
					 atIndex:row];
	} else {
		[textColors addObject:	
		[NSColor colorWithCalibratedRed:[[tColor objectAtIndex:0] intValue] / 65535.0 
								  green:[[tColor objectAtIndex:1] intValue] / 65535.0 
								   blue:[[tColor objectAtIndex:2] intValue] / 65535.0 
								  alpha:1.0]
					 ];
	}
}

-(void)deleteRow:(int)row{
	[textColors removeObjectAtIndex:--row];
	[cellColors removeObjectAtIndex:row];
}

@end

and here’s the AppleScript. I’ve added two buttons to demonstrate how to insert & delete.
The table column is named: table column “coloredCells” of table view “tv” of scroll view “sv” of window “main”
The buttons: button “insert” of window “main” and button “delete” of window “main”

global ds

on awake from nib theObject
	set theStartContent to {"1 red background", "2 green text", "3 blue text", "4 red text", "5 green background", "6 blue background", "7 nothing", "8 blue text", "9 red background"}
	set theRow to 1
	set theColumn to table column "coloredCells" of table view "tv" of scroll view "sv" of window "main"
	call method "initColors" of theColumn
	repeat with nextObj in theStartContent
		my setColors(nextObj, theRow, theColumn)
		set theRow to theRow + 1
	end repeat
	set content of theObject to theStartContent
	set ds to data source of theObject
end awake from nib

on cell value changed theObject row theRow table column tableColumn value theValue
	my setColors(theValue, theRow, tableColumn)
end cell value changed

on clicked theObject
	if (name of theObject) is "delete" then
		tell table view "tv" of scroll view "sv" of window "main"
			set sel to selected rows
			repeat with n from (count of sel) to 1 by -1
				delete data row (item n of sel) in it's data source
				call method "deleteRow:" of table column "coloredCells" with parameter (item n of sel)
			end repeat
			set selected rows to {}
		end tell
	else if (name of theObject) is "insert" then
		tell table view "tv" of scroll view "sv" of window "main"
			tell it's data source
				set newItem to "new item with " & (some item of {"red", "green", "blue"}) & (some item of {" text", " background"})
				set insertBefore to (random number (count of data rows)) + 1
				make new data row at before data row insertBefore
				set contents of data row insertBefore to newItem
			end tell
			call method "insertRow:" of table column "coloredCells" with parameter insertBefore
			my setColors(newItem, insertBefore, table column "coloredCells")
		end tell
	end if
end clicked

on setColors(cellcontents, theRow, theColumn)
	tell theColumn
		if cellcontents contains "text" then
			set textCol to (my getColor(cellcontents))
			if textCol is not {} then
				call method "setTextColor:forRow:" of it with parameters {textCol, theRow}
				call method "setColor:forRow:" of it with parameters {{65535, 65535, 65535}, theRow}
			end if
		else if cellcontents contains "background" then
			set bCol to (my getColor(cellcontents))
			if bCol is not {} then
				call method "setColor:forRow:" of it with parameters {bCol, theRow}
				call method "setTextColor:forRow:" of it with parameters {{65535, 65535, 65535}, theRow}
			end if
		else
			call method "setTextColor:forRow:" of it with parameters {{0, 0, 0}, theRow}
			call method "setColor:forRow:" of it with parameters {{65535, 65535, 65535}, theRow}
		end if
		
	end tell
end setColors

on getColor(cellcontents)
	if cellcontents contains "red" then
		return {65535, 0, 0}
	else if cellcontents contains "green" then
		return {0, 65535, 0}
	else if cellcontents contains "blue" then
		return {0, 0, 65535}
	else
		return {}
	end if
end getColor

D.

Hi Dominik,

Are you still there?

I’m wondering: is it possible to adapt your method for changing text color of the selected row of a table?

Any help would be great.

:slight_smile:

just to change the text color of the selected cell(s) try this


on will display cell theObject cell theCell row theRow table column tableColumn
	if theCell is highlighted then
		set text color of theCell to {0, 0, 0}
	end if
end will display cell

as far as I remember you have to connect the event handler of the table view in Interface Builder