Proxy Data Source for a Simple Data Grid

Description: creating a proxy data source for a data grid. The previous stage is creating a data source.

The ProxyDataSource.h File

#import <NuGridView/NuGridExpandableCell.h>
#import <NuGridView/NuGridProxyDataSource.h>
#import "DataSource.h"
// Determine structure corresponding to cell range
struct NuGridRange {
    NuGridExpandableCell *startCell;
    NuGridExpandableCell *endCell;
};
@interface ProxyDataSource : NSObject<NuGridProxyDataSource> {
    DataSource *_dataSource; // Link to data source
    NSMutableArray *_updateObserver;
    NSMutableArray *_fixedRows; // Array of fixed rows
    NSMutableArray *_fixedColumns; // Array of fixed columns
    unsigned long long _styleOrder; // Style order
    NSMutableDictionary *_styles; // Styles dictionary
    NSMutableDictionary *_stylesSettingOrder; // Style settings dictionary
}
@property(assign) NuGridCell *defaultCell; // Default cell
@property(assign) struct NuGridRange gridRange; // Range of grouped cells
// Fixes row with specified number
- (void)gridView:(NuGridView *)gridView fixRow:(NSInteger)rowNumber;
// Fixes column with specified number
- (void)gridView:(NuGridView *)gridView fixColumn:(NSInteger)columnNumber;
@end

The ProxyDataSource.m File

#import &quot;ProxyDataSource.h&quot;
#import &lt;NuGridView/NuGridView.h&gt;
#import &lt;NuGridView/NuGridSolidColorBrush.h&gt;
#import &lt;NuGridView/NuGridCellWithSparkline.h&gt;
#import &lt;NuGridView/NuGridSparklineHelper.h&gt;
#import &lt;NuGridView/NuGridCellImage.h&gt;
#import &quot;CustomCell.h&quot;
#import &quot;SparklineCell.h&quot;
@implementation ProxyDataSource
@synthesize defaultCell = _defaultCell; // Default cell
@synthesize gridRange = _gridRange; // Table cell range
- (id) init
{
    self = [super init];
    if(self) {
        _dataSource = nil;
        _updateObserver = [NSMutableArray new];
        _defaultCell = nil;
        // Create array of fixed rows
        _fixedRows = [NSMutableArray new];
        // Create array of fixed columns
        _fixedColumns = [NSMutableArray new];
        _styles = [NSMutableDictionary new];
        _stylesSettingOrder = [NSMutableDictionary new];
        _styleOrder = 0;
        
        return self;
    }
    return nil;
}
// Returns data source
- (id&lt;NuGridDataSource&gt;)dataSource
{
    return _dataSource;
}
// Sets data source
- (void)setDataSource:(id&lt;NuGridDataSource&gt;)dataSource
{
    _dataSource = dataSource;
}
// Adds observer to corresponding array of observers
- (void)addObserver:(id&lt;NuGridUpdateObserver&gt;)observer
{
    [_updateObserver addObject:observer];
}
// Removes observer from corresponding array of observers
- (void)removeObserver:(id&lt;NuGridUpdateObserver&gt;)observer
{
    [_updateObserver removeObject:observer];
}
- (void)beforeUpdate:(NSString *)key {
    
    for (id&lt;NuGridUpdateObserver&gt; observer in _updateObserver) {
        [observer beforeUpdate:key];
    }
}
- (void)afterUpdate:(NSString*)key {
    
    for (id&lt;NuGridUpdateObserver&gt; observer in _updateObserver) {
        [observer afterUpdate:key];
    }
}
// Returns whether data source contains row headers
- (BOOL)gridViewHasRowHeader:(NuGridView *)gridView
{
    return [_dataSource gridViewHasRowHeader:gridView];
}
// Returns whether data source contains columns
- (BOOL)gridViewHasColumnHeader:(NuGridView *)gridView
{
    return [_dataSource gridViewHasColumnHeader:gridView];
}
// Returns number of rows
- (NSInteger)gridViewRowCount:(NuGridView *)gridView
{
    return [_dataSource gridViewRowCount:gridView];
}
// Returns number of columns
- (NSInteger)gridViewColumnCount:(NuGridView *)gridView
{
    return [_dataSource gridViewColumnCount:gridView];
}
// Returns number of header rows
- (NSInteger)gridViewRowHeaderCount:(NuGridView *)gridView
{
    return [_dataSource gridViewRowHeaderCount:gridView];
}
// Returns number of header columns
- (NSInteger)gridViewColumnHeaderCount:(NuGridView *)gridView
{
    return [_dataSource gridViewColumnHeaderCount:gridView];
}
// Returns row number by index
- (NSInteger)gridView:(NuGridView *)gridView rowIndexByNumber:(NSInteger)rowNumber
{
    return rowNumber;
}
// Returns column number by index
- (NSInteger)gridView:(NuGridView *)gridView columnIndexByNumber:(NSInteger)columnNumber
{
    return columnNumber;
}
// Returns column index by its number
- (NSInteger)gridView:(NuGridView *)gridView columnNumberByIndex:(NSInteger)columnIndex
{
    return columnIndex;
}
// Returns row index by its number
- (NSInteger)gridView:(NuGridView *)gridView rowNumberByIndex:(NSInteger)rowIndex
{
    return rowIndex;
}
// Sets default style
- (void) gridView:(NuGridView *)gridView setDefaultStyle:(NuGridCellStyle *)style
{
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Default&quot;];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets default style for column header
- (void) gridView:(NuGridView *)gridView setDefaultStyleForColumnHeader:(NuGridCellStyle *)style
{
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Column header&quot;];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets default style for row header
- (void) gridView:(NuGridView *)gridView setDefaultStyleForRowHeader:(NuGridCellStyle *)style
{
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Row header&quot;];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets table formatting theme
- (void)gridView:(NuGridView *)gridView setTheme:(NuGridTheme *)theme {
    [self gridView:gridView setDefaultStyle:[theme defaultStyle]];
    [self gridView:gridView setDefaultStyleForRowHeader:[theme defaultRowHeaderStyle]];
    [self gridView:gridView setDefaultStyleForColumnHeader:[theme defaultColumnHeaderStyle]];
}
// Returns column header style
- (NuGridCellStyle *)styleForColumnHeader:(NSInteger)columnNumber {
    
    NSMutableArray *orderArray = [[NSMutableArray new] autorelease];
    NSNumber *order = [_stylesSettingOrder objectForKey:[NSString stringWithFormat:@&quot;Column_header_%d&quot;, columnNumber]];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:@&quot;Column header&quot;];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:@&quot;Default&quot;];
    if (order) {
        [orderArray addObject:order];
    }
    if (orderArray.count &gt; 0) {
        [orderArray sortUsingSelector:@selector(compare:)];
        
        NSArray *actualKeys = [_stylesSettingOrder allKeysForObject:[orderArray lastObject]];
        if (actualKeys.count &gt; 0) {
            NSString *actualKey = [actualKeys objectAtIndex:0];
            
            NuGridCellStyle *style = [_styles objectForKey:actualKey];
            if (style) {
                return style;
            }
        }
    }
    return [[NuGridCellStyle new] autorelease];
}
// Returns row header style
- (NuGridCellStyle *)styleForRowHeader:(NSInteger)rowNumber {
    NSMutableArray *orderArray = [[NSMutableArray new] autorelease];
    NSNumber *order = [_stylesSettingOrder objectForKey:[NSString stringWithFormat:@&quot;Row_header_%d&quot;, rowNumber]];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:@&quot;Row header&quot;];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:@&quot;Default&quot;];
    if (order) {
        [orderArray addObject:order];
    }
    if (orderArray.count &gt; 0) {
        [orderArray sortUsingSelector:@selector(compare:)];
        
        NSArray *actualKeys = [_stylesSettingOrder allKeysForObject:[orderArray lastObject]];
        if (actualKeys.count &gt; 0) {
            NSString *actualKey = [actualKeys objectAtIndex:0];
            
            NuGridCellStyle *style = [_styles objectForKey:actualKey];
            if (style) {
                return style;
            }
        }
    }
    return [[NuGridCellStyle new] autorelease];
}
// Returns table cell style
- (NuGridCellStyle *)styleForCellInRow:(NSInteger)rowNumber inColumn:(NSInteger)columnNumber {
    NSMutableArray *orderArray = [[NSMutableArray new] autorelease];
    NSNumber *order = [_stylesSettingOrder objectForKey:[NSString stringWithFormat:@&quot;Cell_%d:%d&quot;, rowNumber, columnNumber]];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:[NSString stringWithFormat:@&quot;Column_%d&quot;, columnNumber]];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:[NSString stringWithFormat:@&quot;Row_%d&quot;, rowNumber]];
    if (order) {
        [orderArray addObject:order];
    }
    order = [_stylesSettingOrder objectForKey:@&quot;Default&quot;];
    if (order) {
        [orderArray addObject:order];
    }
    if (orderArray.count &gt; 0) {
        [orderArray sortUsingSelector:@selector(compare:)];
        
        NSArray *actualKeys = [_stylesSettingOrder allKeysForObject:[orderArray lastObject]];
        if (actualKeys.count &gt; 0) {
            NSString *actualKey = [actualKeys objectAtIndex:0];
            
            NuGridCellStyle *style = [_styles objectForKey:actualKey];
            if (style) {
                return style;
            }
        }
    }
    return [[NuGridCellStyle new] autorelease];
}
// Applies style to table cell
- (void)gridView:(NuGridView *)gridView applyStyleForCell:(NuGridCell *)cell {
    NuGridCellStyle *style = nil;
    if ([cell isColumnHeader]) {
        style = [[self styleForColumnHeader:[cell columnNumber]] retain];
    }
    else if ([cell isRowHeader]) {
        style = [[self styleForRowHeader:[cell rowNumber]] retain];
    }
    else {
        style = [[self styleForCellInRow:[cell rowNumber] inColumn:[cell columnNumber]]retain];
    }
    // Apply style to table cell
    [cell applyStyle:style];
    // Set alternating colors
    [self applyBackgroundColorInterchangeForCell:cell];
    // Set gradient used for conditional formatting
    [self applyGradientForCell:cell];
    // Add images for conditional formatting
    [self addConditionImageForCell:cell InGrid:gridView];
    // Apply brush for table cell fill where possible
    if(style != nil &amp;&amp; [cell isKindOfClass:[CustomCell class]] &amp;&amp; [style backgroundBrush] != nil) {
        [(CustomCell *)cell setBrush:[style backgroundBrush]];
        [(CustomCell*)cell setNeedsDisplay];
    }
    // Change cell text according to its type
    [self gridView: gridView applyTypeForCell:(CustomCell*)cell];
    [style release];
};
// Implements  alternating colors
-(void) applyBackgroundColorInterchangeForCell:(NuGridCell *) cell {
    NuGridCellStyle *style = [cell style];
    if([style backgroundColorInterchange]) {
        if([cell rowNumber] % 2 ==0) {
            if ([style firstColor] != nil) {
                [cell setBackgroundColor:[style firstColor]];                
            }
        } else {
            if([style secondColor] != nil) {
                [cell setBackgroundColor:[style secondColor]];
            }
        };
    }
}
// Set gradient fill for cells used for conditional formatting
-(void) applyGradientForCell:(NuGridCell *) cell {
    if ([[cell style] isGradientConfigured] &amp;&amp;
    ([[cell style] gradientRowBackground] | [[cell style] gradientColumnBackground])) {
        // Get table cell value
        double value = [(NSNumber *)[cell value] doubleValue];
        double max = 0.0;
        
        UIColor *lowColor = cell.style.minColor;
        UIColor *midColor = cell.style.midColor;
        UIColor *topColor = cell.style.maxColor;
        
        if (value &gt;= cell.style.midGradValue) {
            max = cell.style.maxGradValue - cell.style.midGradValue;
            value -= cell.style.midGradValue;
            double q = value / max;
            cell.backgroundColor = [self transitionColorFrom:midColor to:topColor position:q for:cell];
            } else {
            max = cell.style.midGradValue - cell.style.minGradValue;
            value -= cell.style.minGradValue;
            double q = value / max;
            cell.backgroundColor = [self transitionColorFrom:lowColor to:midColor position:q for:cell];
        }
    }
}
// Add images used for conditional formatting
-(void) addConditionImageForCell:(NuGridCell *) cell InGrid:(NuGridView *) gridView{
    if ([cell isKindOfClass:[NuGridImageTextCell class]]) {
        NuGridCellStyle *style = [cell style];
        if(([style rowWithImageCondition] | [style columnWithImageCondition]) &amp;&amp; [[style imageCollection] count] &gt; 0) {
            double step = ([style maxGradValue] - [style minGradValue]) / (cell.style.imageCollection.count - 1);
            int part = 0;
            double doubleVal = [(NSNumber *)cell.value doubleValue] - cell.style.minGradValue;
            part = doubleVal / step;
            int index = cell.style.imageCollection.count - 1 - part;
            // Determine conditional formatting image key
            NSString *imageKey = @&quot;arrow0&quot;;
            // Remove old image
            [(NuGridImageTextCell *)cell removeImage:imageKey];
            // Add new image
            [(NuGridImageTextCell *)cell addImage:[[style imageCollection] objectAtIndex:index]
            outputSize:CGSizeMake(12, 12)
            alignment:[style imageAlignment]
            zPosition:[style imageZOrder]
            border:2.0
            key:imageKey];
        }
    }
}
// Determines table cell color by its value
- (UIColor *) transitionColorFrom: (UIColor *) srcCol to: (UIColor *) dstCol position: (double) q for: (NuGridCell*) cell {
    if(srcCol != nil &amp; dstCol != nil) {
        const CGFloat *src = CGColorGetComponents([srcCol CGColor]);
        const CGFloat *dst = CGColorGetComponents([dstCol CGColor]);
        CGFloat result[4];
        for (int i=0; i&lt;4; i++) {
            double s = src[i];
            double d = dst[i];
            double delta = d-s;
            result[i]=s+delta*q;
        }
        return [UIColor colorWithRed:result[0] green:result[1] blue:result[2] alpha:result[3]];
        } else {
        return [cell backgroundColor];
    }
}
// Changes cell text according to its type
-(void) gridView: (NuGridView*)gridView applyTypeForCell:(CustomCell*)cell {
    if ([[cell style] cellType] == NuCellTypePercentageCell) {
        // Change cell text
        [cell setText:[NSString stringWithFormat: @&quot;%@%%&quot;, [cell value]]];
    }
}
// Sets table cell style
- (void)gridView:(NuGridView *)gridView setStyle:(NuGridCellStyle *)style forCellInRow:(NSInteger)rowNumber column:(NSInteger)columnNumber {
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Cell_%d:%d&quot;, rowNumber, columnNumber];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets table row style
- (void)gridView:(NuGridView *)gridView setStyle:(NuGridCellStyle *)style forRow:(NSInteger)rowNumber {
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Row_%d&quot;, rowNumber];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets table column style
- (void)gridView:(NuGridView *)gridView setStyle:(NuGridCellStyle *)style forColumn:(NSInteger)columnNumber {
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Column_%d&quot;, columnNumber];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets row header style
- (void)gridView:(NuGridView *)gridView setStyle:(NuGridCellStyle *)style forHeaderForRow:(NSInteger)rowNumber {
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Row_header_%d&quot;, rowNumber];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Sets column header style
- (void)gridView:(NuGridView *)gridView setStyle:(NuGridCellStyle *)style forHeaderForColumn:(NSInteger)columnNumber {
    [self beforeUpdate:@&quot;dataSource&quot;];
    NSString *key = [NSString stringWithFormat:@&quot;Column_header_%d&quot;, columnNumber];
    [_styles setObject:style forKey:key];
    [_stylesSettingOrder setObject:[NSNumber numberWithUnsignedLongLong:_styleOrder++] forKey:key];
    [self afterUpdate:@&quot;dataSource&quot;];
}
// Returns table row style
- (NuGridCellStyle *)gridView:(NuGridView *)gridView getStyleForRow:(NSInteger)rowNumber {
    NSString *key = [NSString stringWithFormat:@&quot;Row_%d&quot;, rowNumber];
    NuGridCellStyle *style = (NuGridCellStyle *)[_styles objectForKey:key];
    if (style == nil) {
        style = [self gridViewGetDefaultStyle:gridView];
    }
    return style;
}
// Returns table column style
- (NuGridCellStyle *)gridView:(NuGridView *)gridView getStyleForColumn:(NSInteger)columnNumber {
    NSString *key = [NSString stringWithFormat:@&quot;Column_%d&quot;, columnNumber];
    NuGridCellStyle *style = (NuGridCellStyle *)[_styles objectForKey:key];
    if (style == nil) {
        style = [self gridViewGetDefaultStyle:gridView];
    }
    return style;
}
// Returns table cell style
- (NuGridCellStyle *)gridView:(NuGridView *)gridView getStyleForCellInRow:(NSInteger)rowNumber column:(NSInteger)columnNumber {
    NSString *key = [NSString stringWithFormat:@&quot;Cell_%d:%d&quot;, rowNumber, columnNumber];
    NuGridCellStyle *style = (NuGridCellStyle *)[_styles objectForKey:key];
    if (style == nil) {
        style = [self gridViewGetDefaultStyle:gridView];
    }
    return style;
}
// Returns header row style
- (NuGridCellStyle *)gridView:(NuGridView *)gridView getStyleForHeaderForRow:(NSInteger)rowNumber {
    NSString *key = [NSString stringWithFormat:@&quot;Row_header_%d&quot;, rowNumber];
    NuGridCellStyle * style = (NuGridCellStyle *)[_styles objectForKey:key];
    if (style == nil) {
        style = [self gridViewGetDefaultStyleForRowHeader:gridView];
    }
    return style;
}
// Returns header column style
- (NuGridCellStyle *)gridView:(NuGridView *)gridView getStyleForHeaderForColumn:(NSInteger)columnNumber {
    NSString *key = [NSString stringWithFormat:@&quot;Column_header_%d&quot;, columnNumber];
    NuGridCellStyle * style = (NuGridCellStyle *)[_styles objectForKey:key];
    if (style == nil) {
        style = [self gridViewGetDefaultStyleForColumnHeader:gridView];
    }
    return style;
}
// Returns current table formatting theme
- (NuGridTheme *)gridViewTheme:(NuGridView *)gridView {
    // Create table formatting theme
    NuGridTheme *theme = [[NuGridTheme new] autorelease];
    [theme setDefaultRowHeaderStyle:[self gridViewGetDefaultStyleForRowHeader:gridView]];
    [theme setDefaultColumnHeaderStyle:[self gridViewGetDefaultStyleForColumnHeader:gridView]];
    [theme setDefaultStyle:[self gridViewGetDefaultStyle:gridView]];
    return theme;
}
// Returns dafeult theme for table cells
- (NuGridCellStyle *)gridViewGetDefaultStyle:(NuGridView *)gridView {
    NSString *key = [NSString stringWithFormat:@&quot;Default&quot;];
    return (NuGridCellStyle *)[_styles objectForKey:key];
}
// Returns default theme for row headers
- (NuGridCellStyle *)gridViewGetDefaultStyleForRowHeader:(NuGridView *)gridView {
    NSString *key = [NSString stringWithFormat:@&quot;Row header&quot;];
    return (NuGridCellStyle *)[_styles objectForKey:key];
}
// Returns default theme for column headers
- (NuGridCellStyle *)gridViewGetDefaultStyleForColumnHeader:(NuGridView *)gridView {
    NSString *key = [NSString stringWithFormat:@&quot;Column header&quot;];
    return (NuGridCellStyle *)[_styles objectForKey:key];
}
// Converts fixe column index to normal column index
- (NSInteger)gridView:(NuGridView *)gridView indexForFixedColumnWithIndex:(NSInteger)index {
    if(_fixedColumns &gt; 0) {
        NSNumber *fixedColumnIndex = (NSNumber*)[_fixedColumns objectAtIndex:([_fixedColumns count] - 1)];
        return [fixedColumnIndex intValue];
        } else {
        return  NSNotFound;
    }
}
// Converts fixed row index to normal row index
- (NSInteger)gridView:(NuGridView *)gridView indexForFixedRowWithIndex:(NSInteger)index {
    if(_fixedRows &gt; 0) {
        NSNumber *fixedRowIndex = (NSNumber*)[_fixedRows objectAtIndex:([_fixedRows count] - 1)];
        return [fixedRowIndex intValue];
        } else {
        return  NSNotFound;
    }
}
// Fixes row with specified number
- (void)gridView:(NuGridView *)gridView fixRow:(NSInteger)rowNumber {
    NSNumber *number = [NSNumber numberWithInt:rowNumber];
    if (![_fixedRows containsObject:number]) {
        [self beforeUpdate:@&quot;dataSource&quot;];
        [_fixedRows addObject:number];
        [self afterUpdate:@&quot;dataSource&quot;];
    }
}
// Fixes column with specified number
- (void)gridView:(NuGridView *)gridView fixColumn:(NSInteger)columnNumber {
    NSNumber *number = [NSNumber numberWithInt:columnNumber];
    if (![_fixedColumns containsObject:number]) {
        [self beforeUpdate:@&quot;dataSource&quot;];
        [_fixedColumns addObject:number];
        [self afterUpdate:@&quot;dataSource&quot;];
    }
}
// Returns cell by row number and column number
- (NuGridCell *) gridView:(NuGridView *)gridView cellInRow:(NSInteger)row inColumn:(NSInteger)column
{
    // Get cell from data source
    CustomCell *cell= (CustomCell*)[_dataSource gridView:gridView cellInRow:row inColumn:column];
    // Apply style to this cell
    [self gridView:gridView applyStyleForCell:cell];
    cell = (CustomCell *)[self mergeGridCell:(CustomCell *)cell];
    return cell;
}
// Returns row header cell
- (NuGridCell *) gridView:(NuGridView *)gridView headerForRow:(NSInteger)row number:(NSInteger)number
{
    NuGridCell *cell = [_dataSource gridView:gridView headerForRow:row number:number];
    [self gridView:gridView applyStyleForCell:cell];
    cell = [self mergeGridCell:(NuGridImageTextCell *)cell];
    return cell;
}
// Returns column header cell
- (NuGridCell *) gridView:(NuGridView *)gridView headerForColumn:(NSInteger)column number:(NSInteger)number
{
    NuGridCell *cell = [_dataSource gridView:gridView headerForColumn:column number:number];
    [self gridView:gridView applyStyleForCell:cell];
    cell = [self mergeGridCell:(NuGridImageTextCell *)cell];
    return cell;
}
// Returns left top table cell
- (NuGridCell *) gridView:(NuGridView *)gridView cornerCellInRowNumber:(NSInteger)rowNumber inColumnNumber:(NSInteger)columnNumber
{
    SparklineCell *cell = (SparklineCell *)[_dataSource gridView:gridView cornerCellInRowNumber:rowNumber inColumnNumber:columnNumber];
    // Set style for specified table cell
    [self gridView:gridView applyStyleForCell:cell];
    
    [cell setIsUseSparkline:NO];
    cell = (SparklineCell *)[self mergeGridCell:(SparklineCell *)cell];
    return cell;
}
// Extends cell settings
-(NuGridCell *)mergeGridCell:(NuGridCell *)gridCell {
    if (_defaultCell != nil &amp;&amp; gridCell != nil) {
        if([gridCell isKindOfClass:[SparklineCell class]] &amp; [_defaultCell isKindOfClass:[SparklineCell class]]) {
            [(SparklineCell *)gridCell setIsUseSparkline:[(SparklineCell *)_defaultCell isUseSparkline]];
            [(SparklineCell *)gridCell setRowData:[(SparklineCell *)_defaultCell rowData]];
        }
        if([[self defaultCell] isKindOfClass:[CustomCell class]]) {
            // Get cell image
            NSMutableDictionary *images = [[self defaultCell] valueForKey:@&quot;m_images&quot;];
            if (images != nil) {
                for (int i = 0; i &lt; [[images allValues] count]; i++) {
                    // Get table cell image
                    NuGridCellImage *imageCell = [images valueForKey:[NSString stringWithFormat:@&quot;cellImage%d&quot;, i]];
                    if(imageCell != nil) {
                        // Get cell number
                        NSNumber *rowNumber = (NSNumber *)[images valueForKey:[NSString stringWithFormat:@&quot;row%d&quot;, i]];
                        // Get column number
                        NSNumber *columnNumber = (NSNumber *)[images valueForKey:[NSString stringWithFormat:@&quot;column%d&quot;, i]];
                        if(([rowNumber intValue] == [gridCell rowNumber]) &amp;&amp;
                        ([columnNumber intValue] == [gridCell columnNumber])) {
                            // Add image to cell
                            if([imageCell border] == 0.f) {
                                [(NuGridImageTextCell*)gridCell addImage:[imageCell image] outputSize:[imageCell size] alignment:[imageCell alignment] zPosition:[imageCell zPosition] leftBorder:[imageCell leftBorder] rightBorder:[imageCell rightBorder] topBorder:[imageCell topBorder] bottomBorder:[imageCell bottomBorder] key:[NSString stringWithFormat:@&quot;NuGridCellImage%d&quot;, i]];
                                } else {
                                [(NuGridImageTextCell*)gridCell addImage:[imageCell image] outputSize:[imageCell size] alignment:[imageCell alignment] zPosition:[imageCell zPosition] border:[imageCell border] key:[NSString    stringWithFormat:@&quot;NuGridCellImage%d&quot;, i]];
                            }
                        }
                    }
                }
            }
        }
        if ([gridCell isKindOfClass:[CustomCell class]] &amp; [_defaultCell isKindOfClass:[CustomCell class]]) {
            if ([gridCell rowNumber] == [_defaultCell rowNumber] &amp;
            [gridCell columnNumber] == [_defaultCell columnNumber]) {
                [(CustomCell*)gridCell setIsExpandableRow:[(CustomCell*)_defaultCell isExpandableRow]];
                [(CustomCell*)gridCell setIsExpandableColumn:[(CustomCell*)_defaultCell isExpandableColumn]];
                [(CustomCell*) gridCell setIsExpanded:[(CustomCell*)_defaultCell isExpanded]];
            }
        }
    }
    return gridCell;
};
// Returns whether specified column is fixed
- (BOOL)gridView:(NuGridView *)gridView isColumnWithIndexFixed:(NSInteger)columnIndex
{
    if (_fixedColumns.count) {
        int columnNumber = [self gridView:gridView columnNumberByIndex:columnIndex];
        NSNumber *number = [NSNumber numberWithInt:columnNumber];
        return [_fixedColumns containsObject:number];
    }
    return NO;
}
// Returns whether specified row is fixed
- (BOOL)gridView:(NuGridView *)gridView isRowWithIndexFixed:(NSInteger)rowIndex
{
    if (_fixedRows.count) {
        int rowNumber = [self gridView:gridView rowNumberByIndex:rowIndex];
        NSNumber *number = [NSNumber numberWithInt:rowNumber];
        return [_fixedRows containsObject:number];
    }
    return NO;
}
// Returns number of fixed table rows
- (NSInteger)gridViewFixedRowCount:(NuGridView *)gridView {
    return [_fixedRows count];
};
// Returns number of fixed table columns
- (NSInteger)gridViewFixedColumnCount:(NuGridView *)gridView {
    return [_fixedColumns count];
}
// Expands table row
- (void)gridView:(NuGridView *)gridView expandRow:(NSInteger)rowNumber {
    [self beforeUpdate:@&quot;DataSource&quot;];
    [[self gridRange].startCell setIsExpanded:YES];
    [[self gridRange].startCell setIsExpandableRow:YES];
    if ([[self gridRange].startCell rowNumber] == rowNumber) {
        NuGridCell *toCell = [[NuGridCell new] autorelease];
        [toCell setRowNumber:[self gridRange].endCell.rowNumber];
        [toCell setColumnNumber:[self gridRange].startCell.columnNumber];
        [_dataSource expandRangeFromCell:[self gridRange].startCell ToCell:toCell];
        _defaultCell = [self gridRange].startCell;
    }
    [self afterUpdate:@&quot;DataSource&quot;];
}
// Collapses table row
- (void) gridView:(NuGridView *)gridView collapseRow:(NSInteger)rowNumber {
    [self beforeUpdate:@&quot;DataSource&quot;];
    [[self gridRange].startCell setIsExpanded:NO];
    [[self gridRange].startCell setIsExpandableRow:YES];
    if ([[self gridRange].startCell rowNumber] == rowNumber) {
        NuGridCell *toCell = [[NuGridCell new] autorelease];
        [toCell setRowNumber:[self gridRange].endCell.rowNumber];
        [toCell setColumnNumber:[self gridRange].startCell.columnNumber];
        [_dataSource collapseRangeFromCell:[self gridRange].startCell ToCell:toCell];
        _defaultCell = [self gridRange].startCell;
    }
    [self afterUpdate:@&quot;DataSource&quot;];
}
// Expands table column
- (void)gridView:(NuGridView *)gridView expandColumn:(NSInteger)columnNumber {
    [self beforeUpdate:@&quot;DataSource&quot;];
    [[self gridRange].startCell setIsExpanded:YES];
    [[self gridRange].startCell setIsExpandableColumn:YES];
    if ([[self gridRange].startCell columnNumber] == columnNumber) {
        NuGridCell *toCell = [[NuGridCell new] autorelease];
        [toCell setRowNumber:[self gridRange].startCell.rowNumber];
        [toCell setColumnNumber:[self gridRange].endCell.columnNumber];
        [_dataSource expandRangeFromCell:[self gridRange].startCell ToCell:toCell];
        _defaultCell = [self gridRange].startCell;
    }
    [self afterUpdate:@&quot;DataSource&quot;];
}
// Collapses table column
- (void) gridView:(NuGridView *)gridView collapseColumn:(NSInteger)columnNumber {
    [self beforeUpdate:@&quot;DataSource&quot;];
    [[self gridRange].startCell setIsExpanded:NO];
    [[self gridRange].startCell setIsExpandableColumn:YES];
    if ([[self gridRange].startCell columnNumber] == columnNumber) {
        NuGridCell *toCell = [[NuGridCell new] autorelease];
        [toCell setRowNumber:[self gridRange].startCell.rowNumber];
        [toCell setColumnNumber:[self gridRange].endCell.columnNumber];
        [_dataSource collapseRangeFromCell:[self gridRange].startCell ToCell:toCell];
        _defaultCell = [self gridRange].startCell;
    }
    [self afterUpdate:@&quot;DataSource&quot;];
}
- (void)dealloc
{
    [[self defaultCell] release];
    [super dealloc];
}
@end

See also:

Creating a Simple Data Grid