Skip to content

Instantly share code, notes, and snippets.

@Thesaurus
Created December 4, 2015 13:19
Show Gist options
  • Save Thesaurus/f6e6d60495cb8f29eb48 to your computer and use it in GitHub Desktop.
Save Thesaurus/f6e6d60495cb8f29eb48 to your computer and use it in GitHub Desktop.
Using NSTableView as a grid control.
//
// TSGridView.h
//
// Created by Jonathan Mitchell on 06/07/2015.
// Copyright (c) 2015 Thesaurus Software Limited. All rights reserved.
//
#import <Cocoa/Cocoa.h>
extern NSString *TSGridColumns;
extern NSString *TSGridIdentifier;
extern NSString *TSGridWidth;
extern NSString *TSGridObject;
extern NSString *TSGridPath;
extern NSString *TSGridOptions;
extern NSString *TSGridItems;
extern NSString *TSGridItemValue;
extern NSString *TSGridItemFormatter;
extern NSString *TSGridItemControlSize;
extern NSString *TSGridItemValueBinding;
extern NSString *TSGridItemType;
extern NSString *TSGridItemTypeLabel;
extern NSString *TSGridItemTypeInput;
@interface TSGridView : NSTableView <NSTabViewDelegate, NSObject>
- (id)initWithDictionary:(NSDictionary *)config;
@end
//
// TSGridView.m
//
// Created by Jonathan Mitchell on 06/07/2015.
// Copyright (c) 2015 Thesaurus Software Limited. All rights reserved.
//
#import "TSGridView.h"
#import "BPTableCellView.h"
#import "BPPercentageInputTableCellView.h"
NSString *TSGridColumns = @"columns";
NSString *TSGridIdentifier = @"identifier";
NSString *TSGridWidth = @"width";
NSString *TSGridItems = @"items";
NSString *TSGridObject = @"object";
NSString *TSGridPath = @"path";
NSString *TSGridOptions = @"options";
NSString *TSGridItemValue = @"value";
NSString *TSGridItemFormatter = @"formatter";
NSString *TSGridItemControlSize = @"controlSize";
NSString *TSGridItemValueBinding = @"valueBinding";
NSString *TSGridItemType = @"type";
NSString *TSGridItemTypeLabel = @"label";
NSString *TSGridItemTypeInput = @"input";
@interface TSGridView()
@property (strong) NSArray *gridRows;
@end
@implementation TSGridView
#pragma mark -
#pragma mark Lifecycle
- (id)initWithDictionary:(NSDictionary *)config
{
self = [super init];
if (self) {
[self configure];
[self configureWithDictionary:config];
}
return self;
}
- (void)dealloc
{
}
- (void)configureWithDictionary:(NSDictionary *)config
{
// define columns
NSArray *columns = config[TSGridColumns];
for (NSDictionary *columnInfo in columns) {
NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:columnInfo[TSGridIdentifier]];
column.minWidth = 50;
column.resizingMask = NSTableColumnAutoresizingMask;
column.width = [columnInfo[TSGridWidth] doubleValue];
[self addTableColumn:column];
}
}
- (void)configure
{
// basic grid configuration
self.focusRingType = NSFocusRingTypeNone;
self.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
self.gridStyleMask = NSTableViewSolidVerticalGridLineMask | NSTableViewSolidHorizontalGridLineMask;
self.gridColor = [NSColor lightGrayColor];
self.rowHeight = 30;
self.columnAutoresizingStyle = NSTableViewUniformColumnAutoresizingStyle;
self.intercellSpacing = NSMakeSize(0, 0);
// in order to keep things self contained we will act as our own delegate
self.delegate = (id)self;
// register cell views for the supported grid item type
NSNib *columnCellNib = nil;
columnCellNib = [[NSNib alloc] initWithNibNamed:[BPTableCellView className] bundle:nil];
[self registerNib:columnCellNib forIdentifier:TSGridItemTypeLabel];
columnCellNib = [[NSNib alloc] initWithNibNamed:[BPPercentageInputTableCellView className] bundle:nil];
[self registerNib:columnCellNib forIdentifier:TSGridItemTypeInput];
}
#pragma mark -
#pragma mark Overrides
- (void)reloadData
{
// called when content binding changes
NSDictionary *bindingInfo = [self infoForBinding:NSContentBinding];
NSObject *bindingObject = bindingInfo[NSObservedObjectKey];
NSString *bindingKeyPath = bindingInfo[NSObservedKeyPathKey];
self.gridRows = [bindingObject valueForKeyPath:bindingKeyPath];
[super reloadData];
}
#pragma mark -
#pragma mark NSTableView delegate
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// use tableColumn identifier to access row item data
NSDictionary *gridRow = self.gridRows[row];
NSDictionary *rowItems = gridRow[TSGridItems];
NSDictionary *rowItem = rowItems[tableColumn.identifier];
// make view according to row item type rather than by tableColumn idenitifer
NSString *gridItemType = rowItem[TSGridItemType];
NSView *view = nil;
// query the row item
id itemValue = rowItem[TSGridItemValue];
NSFormatter *formatter = rowItem[TSGridItemFormatter];
NSControlSize controlSize = NSRegularControlSize;
if (rowItem[TSGridItemControlSize]) {
controlSize = [rowItem[TSGridItemControlSize] integerValue];
}
float fontSize = [NSFont systemFontSizeForControlSize:controlSize];
NSFont *font = [NSFont systemFontOfSize:fontSize];
// get binding info
NSDictionary *valueBinding = rowItem[TSGridItemValueBinding];
NSString *valueBindingKeyPath = nil;
NSDictionary *valueBindingOptions = nil;
if (valueBinding) {
valueBindingKeyPath = valueBinding[TSGridPath];
valueBindingOptions = valueBinding[TSGridOptions];
}
NSObject *bindTo = gridRow[TSGridObject];
// load required table cell view
if ([gridItemType isEqualToString:TSGridItemTypeLabel] ||
[gridItemType isEqualToString:TSGridItemTypeInput]) {
// make the cell
BPTableCellView *cellView = [self makeViewWithIdentifier:gridItemType owner:nil];
// cells can get recycled
cellView.textField.objectValue = nil;
[cellView.textField unbind:NSValueBinding];
cellView.textField.formatter = formatter;
// set size
((NSCell *)(cellView.textField.cell)).controlSize = controlSize;
cellView.textField.font = font;
// set value
if (itemValue) {
cellView.textField.objectValue = itemValue;
}
// binding
if (bindTo && valueBindingKeyPath) {
[cellView.textField bind:NSValueBinding toObject:bindTo withKeyPath:valueBindingKeyPath options:valueBindingOptions];
}
view = cellView;
}
return view;
}
- (NSSize)intrinsicContentSize
{
CGFloat h = self.gridRows.count * self.rowHeight;
return NSMakeSize(NSViewNoInstrinsicMetric, h);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment