How can I change the color of a cell in my NSTableView?
In your NSTableViewDelegate for the NSTableView, implement this method:
- (void)tableView:(NSTableView *)tableView
willDisplayCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
The NSTableView calls this on its delegate before displaying each cell so that you can affect its appearance. Assuming you're using NSTextFieldCells, for the cell that you want to change call:
[cell setBackgroundColor:...];
Or, if you want to change the text color:
[cell setTextColor:...];
If you want columns to have different appearances, or if all of the columns aren't NSTextFieldCells, use [tableColumn identifier] to, er, identify the column. You can set the identifier in Interface Builder by selecting the table column.
// TESTED - Swift 3 solution...for changing color of cell text in a single column. All columns in my tableview have a unique identifier
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let myCell:NSTableCellView = tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: self) as! NSTableCellView
if tableColumn?.identifier == "MyColumn" {
let results = arrayController.arrangedObjects as! [ProjectData]
let result = results[row]
if result.ebit < 0.0 {
myCell.textField?.textColor = NSColor.red
} else {
myCell.textField?.textColor = NSColor.black
}
}
return myCell
}
//for VIEW based TableViews using Objective C
//in your NSTableViewDelegate, implement the following
//this customization makes the column numbers red if negative.
- (NSView *)tableView:(NSTableView *)inTableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
NSTableCellView *result = nil;
if ([tableColumn.title isEqualToString: #"Amount"]) {
//pick one of the following methods to identify the NSTableCellView
//in .xib file creation, leave identifier "blank" (default)
result = [inTableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
//or set the Amount column's NSTableCellView's identifier to "Amount"
result = [inTableView makeViewWithIdentifier:#"Amount" owner:self];
id aRecord = [[arrayController arrangedObjects] objectAtIndex:row];
//test the relevant field's value
if ( aRecord.amount < 0.0 )
[[result textField] setTextColor:[NSColor colorWithSRGBRed:1.0 green:0.0 blue:0.0 alpha:1.0]];
} else {
//allow the defaults to handle the rest of the columns
result = [inTableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
}
return result;
}
Try using a custom NSView for that, or NSTableView's -setBackgroundColor: method.
Related
In my application I have a view based NSTableView with one column. The highlight color of the rows is set to regular (blue). I need to change that color to my custom color. In the interface builder I tried changing it but the only options are "None, regular and source list".
I tried this post solution with no success:
https://stackoverflow.com/a/9594543/3065901
I read that I have to use this delegate method but I dont know how to use this.
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
I tried drawing the row in that method but i get invalid context warnings and the row still keeps with the same higlight.
Please post a simple example of how to use this delegate method:
Need help please. Thanks in advance.
From this link.
MyNSTableRowView.h
#import <Cocoa/Cocoa.h>
#interface MyNSTableRowView : NSTableRowView
#end
MyNSTableRowView.m
#import "MyNSTableRowView.h"
#implementation MyNSTableRowView
- (id)init
{
if (!(self = [super init])) return nil;
return self;
}
- (void)drawSelectionInRect:(NSRect)dirtyRect {
if (self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone) {
NSRect selectionRect = NSInsetRect(self.bounds, 2.5, 2.5);
[[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke];
[[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill];
NSBezierPath *selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect
xRadius:6 yRadius:6];
[selectionPath fill];
[selectionPath stroke];
}
}
#end
AppDelegate.m
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
MyNSTableRowView *rowView = [[MyNSTableRowView alloc]init];
return rowView;
}
Here is user3065901's answer in Swift 3:
class MyNSTableRowView: NSTableRowView {
override func drawSelection(in dirtyRect: NSRect) {
if self.selectionHighlightStyle != .none {
let selectionRect = NSInsetRect(self.bounds, 2.5, 2.5)
NSColor(calibratedWhite: 0.65, alpha: 1).setStroke()
NSColor(calibratedWhite: 0.82, alpha: 1).setFill()
let selectionPath = NSBezierPath.init(roundedRect: selectionRect, xRadius: 6, yRadius: 6)
selectionPath.fill()
selectionPath.stroke()
}
}
}
NSTableViewDelegate:
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
return MyNSTableRowView()
}
If you are using cell based tableview, set the NSTableView selectionHighLightStyle to None
NSTableView, and override drawRow sample below
- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect {
NSColor* bgColor = Nil;
// Set the color only when its first responder and key window
if (self == [[self window] firstResponder] && [[self window] isMainWindow] && [[self window] isKeyWindow])
{
bgColor = [NSColor brownColor];
}
else
{
bgColor = [NSColor windowBackgroundColor];;
}
NSIndexSet* selectedRowIndexes = [self selectedRowIndexes];
if ([selectedRowIndexes containsIndex:row])
{
[bgColor setFill];
NSRectFill([self rectOfRow:row]);
}
[super drawRow:row clipRect:clipRect];
}
Add below line in your tableview method
ObjectiveC
[yourtableview setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
Swift
yourtableview.selectionHighlightStyle = .none
I have a NSTableView whose cells are view-based.
DataSource & Delegate are connected, but I'm not able to display the cell's textField string value.
This is the code in Objective-C, working:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 10;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *cell = [tableView makeViewWithIdentifier:#"List" owner:self];
[cella.textField setStringValue:"Hey, this is a cell"];
return cell;
}
And here is my code in Swift, not working :
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
return 10 //Casual number
}
func tableView(tableView: NSTableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> NSTableCellView! {
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView!
// setup cell without force unwrapping it
cell.textField.stringValue = "Hey, this is a cell"
println("Method called") //Never printed
return cell
}
This is the result: (table on right side of image)
Note that the comment //setup cell without force unwrapping it makes no sense, I forgot to delete it.
What I am missing ?
Edit: I tried even the following with no success:
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
return 10
}
func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject
{
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView
cell.textField.stringValue = "Hey this is a cell"
return cell;
}
Thank you all.
Alberto
After hours of search, I discovered this method that works !
func tableView(tableView: NSTableView, viewForTableColumn: NSTableColumn, row: Int) -> NSView
{
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView
cell.textField.stringValue = "Hey, this is a cell"
return cell;
}
I see that you found your answer your self but from what I can see your clue was in the Return Value of Objective -C delegate.
- (NSView *)tableView:...
The return value is a NSView.
But you should look at the Swift/Objective -c documentaion.
From the Docs:
Providing Views for Rows and Columns
tableView:viewForTableColumn:row:
Asks the delegate for a view to display the specified row and column.
Declaration
SWIFT
#optional func tableView(_ tableView: NSTableView!,
viewForTableColumn tableColumn: NSTableColumn!,
row row: Int) -> NSView!
OBJECTIVE-C
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
Note the -> NSView! in the swift code also.
The new docs allow you to see the code for Swift and Objective -c side by side or one or the other. You can use a selection tab at the top of the documentation to choose.
It also looks like your code should include the "!" for optionals
If you change the content mode of the table view from view based to cell based the method is called.
This beat me up for an hour. Works with Swift 2.2, probably won't work for earlier or later versions:
let cell = tableView.makeViewWithIdentifier(myid!, owner: nil) // You can't cast this with as! like they want you to
if let mycell = cell as? NSTableCellView {
mycell.textField?.stringValue = text
return mycell
}
I am not really familiar with tables, as I usually make games, but now I want to create a level builder where I need a table view with custom cells. I have created a nib file and I have subclassed NSTableCellView, but I don't know what to do next. All I have is:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:NSMakeRect(self.window.frame.size.width-TABLEWIDTH, 0, TABLEWIDTH, self.window.frame.size.height)];
SpriteTable *sT = [[SpriteTable alloc]initWithFrame:NSMakeRect(self.window.frame.size.width-TABLEWIDTH, 0, TABLEWIDTH, self.window.frame.size.height)];
NSTableView *tableView = [[NSTableView alloc] initWithFrame: sT.bounds];
NSTableColumn* firstColumn = [[[NSTableColumn alloc] initWithIdentifier:#"firstColumn"] autorelease];
[[firstColumn headerCell] setStringValue:#"First Column"];
[tableView addTableColumn:firstColumn];
tableView.dataSource = self;
tableView.delegate = self;
[tableContainer setDocumentView:tableView];
tableContainer.autoresizingMask = NSViewHeightSizable | NSViewMinXMargin;
[self.window.contentView addSubview: tableContainer];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return 4;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
// get an existing cell with the MyView identifier if it exists
CustomCell *result = [tableView makeViewWithIdentifier:#"MyView" owner:self];
// There is no existing cell to reuse so we will create a new one
if (result == nil) {
NSLog(#"result = nil");
// create the new NSTextField with a frame of the {0,0} with the width of the table
// note that the height of the frame is not really relevant, the row-height will modify the height
// the new text field is then returned as an autoreleased object
//result = [[[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 70)] autorelease];
// the identifier of the NSTextField instance is set to MyView. This
// allows it to be re-used
result.identifier = #"MyView";
}
// result is now guaranteed to be valid, either as a re-used cell
// or as a new cell, so set the stringValue of the cell to the
// nameArray value at row
result.imageView.image = [NSImage imageNamed:NSImageNameHomeTemplate];
// return the result.
return result;
}
If any, which delegate methods do I have to implement ? And how do I customize my cell WITH a nib file ?
Do this in ur subview->
#implementation suhasView
#synthesize name,containerView;// container view contains ur subview
- (NSView*) myView
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSNib *theNib = [[NSNib alloc] initWithNibNamed:#"suhas"bundle:bundle];
[theNib instantiateNibWithOwner:self topLevelObjects:nil];
return containerView;
}
In Controller->
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
suhas *a=[[suhas alloc]initWithFrame:NSMakeRect(0,0, 40, 40)];
NSView * v = [a myView];
[a.name setStringValue:#"suhas"];
return v;
}...//sorry for my naming of the class:)
I have a view based nstableview. I want to color entire row based on some condtion for which I have used code below
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
NSTableRowView *view = [[NSTableRowView alloc] initWithFrame:NSMakeRect(1, 1, 100, 50)];
[view setBackgroundColor:[NSColor redColor]];
return view;;
}
The delegate method is called, but table doesn't seem to be using NSTableRowView returned by delegate method.
Main aim here is coloring entire row based on some condition. Whats wrong in above implementation?
For anyone else who hits this and wants a custom NSTableRowView backgroundColor, there are two approaches.
If you don't need custom drawing, simply set rowView.backgroundColor in - (void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row in your NSTableViewDelegate.
Example:
- (void)tableView:(NSTableView *)tableView
didAddRowView:(NSTableRowView *)rowView
forRow:(NSInteger)row {
rowView.backgroundColor = [NSColor redColor];
}
If you do need custom drawing, create your own NSTableRowView subclass with desired drawRect. Then, implement the following in NSTableViewDelegate:
Example:
- (NSTableRowView *)tableView:(NSTableView *)tableView
rowViewForRow:(NSInteger)row {
static NSString* const kRowIdentifier = #"RowView";
MyRowViewSubclass* rowView = [tableView makeViewWithIdentifier:kRowIdentifier owner:self];
if (!rowView) {
// Size doesn't matter, the table will set it
rowView = [[[MyRowViewSubclass alloc] initWithFrame:NSZeroRect] autorelease];
// This seemingly magical line enables your view to be found
// next time "makeViewWithIdentifier" is called.
rowView.identifier = kRowIdentifier;
}
// Can customize properties here. Note that customizing
// 'backgroundColor' isn't going to work at this point since the table
// will reset it later. Use 'didAddRow' to customize if desired.
return rowView;
}
Finally it worked as below
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSView *cellView = (NSView*) [tableView makeViewWithIdentifier:[tableColumn identifier] owner:[tableView delegate]];
CALayer *viewLayer = [CALayer layer];
[viewLayer setBackgroundColor:[[NSColor redcolor] CGColor]];
[cellView setWantsLayer:YES];
[cellView setLayer:viewLayer];
return cellView;
}
Please note.. u need to convert nscolor to cgcolor which you can find in https://gist.github.com/707921 or http://forrst.com/posts/CGColor_Additions_for_NSColor-1eW
If you watch the presentation on view based tableviews from WWDC 2011, you'll see that the main idea is to create the views in Interface Builder, and then obtain them from there. Something like:
[tableView makeViewWithIdentifier:#"GroupRow" owner:self];
Once you have obtained the view, just set its properties and return it.
Notice in this example that it has its own identifier, so remember to set that, but you can also used automatic identifiers.
I don't know if a direct link to the WWDC will work, but the main page is here: https://developer.apple.com/videos/wwdc/2011/ and if you search for "View Based NSTableView Basic to Advanced", you'll find it. It is well worth watching.
I re-wrote the layer approach.
In Swift 3.2
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let greenCell = self.tableview.make(withIdentifier: "green", owner: self)
let layer:CALayer = CALayer()
layer.backgroundColor = NSColor.green.cgColor
greenCell?.wantsLayer = true
greenCell?.layer = layer
return greenCell
}
Don't forget to change the Identifier of the cell according to your storyboard, and in the code identifier "green". And surely, the background color if you want.
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.