Drill down table in shinobidata grid


#1

Hi there…

Does anyone know how to create drilldown table like this using shinobi grid…?? drill down should be applied to the al table like image bellow… any idea how to achive this…???

Thanks… 


#2

Greetings Program!

Last reported in August 29, 2013, Jan said the functionality is not available out of box and suggested using a nested grid although no example/how-to was given: http://www.shinobicontrols.com/forum/shinobicontrols/2013/8/multilevel-sections

Another possibility is to group the grid by the common nested title, hide the section header, indent the column text for the level, show hide the section on selection of the last row in the previous section, and… No that’s getting too complicated.

Insert/delete the nested rows on selection of a row is the easiest I think.

Wg


#3

thanks for your quick reply wizgod…

I saw some BI solution has sectioning views… thats mean in grid have same sections and if we want we can expand that section for ferther details…Any posibility doing that using shinobi grid…??? if it is possible can u give some example…

Thanx…


#4

You could have the sections and expand/collapse them but they wouldn’t be nested, only a flat grouping, and you would have a big gap of collapsed section headers between data; have a look at the DataGridWithSections sample app.

If you only group it by year, you could make a custom header with the column totals so the grouping flows a bit better. If you want to have the sub groupings, it will be a bit more complicated to achieve.

I have a custom expand/collapse SDataGridCell that all it does is toggles an up/down arrow on touch representing an expanded/collapsed state. Setting the level indents the arrows to represent a nested state. Following the suggestion in the previous post about hiding the section header; my thought is to hide the section headers, have this cell as the first column then when it’s selected, hide or show the section it is responsible for. For nested sections with children, the arrow would be indented based on the level to show it visually.

The data would need to have the nested levels, ids and parent ids for it to work. Grouping would need to be done in a way to keep the hierarchy intact so the sections come out in that order…

Level 0 = root with no children

0 - 2000 (id = 1, parent id = 0)
1 - 2008 (id = 2, parent id = 0)
   2 - 2008 Q1 (id = 3, parent id = 2)
     3 - 2008 Jan (id = 4, parent id = 3)
     3 - 2008 Feb (id = 5, parent id = 3)
     3 - 2008 Mar (id = 6, parent id = 3)
   2 - 2008 Q2 (id = 7, parent id = 2)
     3 - 2008 Apr (id = 4, parent id = 7)
     3 - 2008 May (id = 5, parent id = 7)
     3 - 2008 Jun (id = 6, parent id = 7)

I don’t know how well it would work or even could work but I think it’s possible.

Wg


#5

Here is a working version. It’s barely an alpha and not optimized in any way and may not be the best way.

When the expand/collapse cell is tapped and expanded, it will insert the children directly below it (from the parentId) and any child records that were also expanded…

When it’s collapsed, it will loop through all the records after the parent record and remove any whose level is greater than the parent; this way it will catch any children of children sections that are open. It will keep the expanded/collapsed state of the children for when the parent is expanded again.

I hope it helps.

Wg

//
// WGExpandCollapseCell.h
// DrillDownGrid
//
// Copyright (c) 2015 Wizgod Inc. All rights reserved.
//

#import <ShinobiGrids/ShinobiGrids.h>

@protocol WGExpandCollapseCellDelegate <NSObject>

@optional
- (void)didTapExpandCollapseCell:(NSObject *)cell;

@end

@interface WGExpandCollapseCell : SDataGridCell

@property (nonatomic) int recordId;
@property (nonatomic) int parentId;
@property (nonatomic) int level;
@property (nonatomic) bool isExpanded;
@property (nonatomic) bool hasChildren;
@property (nonatomic, strong) UILabel *symbolLabel;
@property (nonatomic, strong) UILabel *textLabel;

@property (nonatomic, strong) id <WGExpandCollapseCellDelegate> delegate;

@end



//
// WGExpandCollapseCell.m
// DrillDownGrid
//
// Copyright (c) 2015 Wizgod Inc. All rights reserved.
//

#import "WGExpandCollapseCell.h"
#import <QuartzCore/QuartzCore.h>

@interface WGExpandCollapseCell() <UIGestureRecognizerDelegate, WGExpandCollapseCellDelegate>

@end

@implementation WGExpandCollapseCell

- (void)setLevel:(int)level {
    if(_level == level) {
        return;
    }
    
    _level = level;
	
	[self updateLabels];
}

- (void)setIsExpanded:(bool)isExpanded {

	_isExpanded = isExpanded;
    
    [self displayExpandCollapseSymbol];
}


- (void)displayExpandCollapseSymbol
{
	NSString *indentSymbol = @"";
	
	// Indent the expand/collapse label.
	if (_hasChildren && _isExpanded) {
		indentSymbol = @"▲";
	}
	else if (_hasChildren && !_isExpanded) {
		indentSymbol = @"▼";
	}
	
	_symbolLabel.text = indentSymbol;
}

- (instancetype)initWithReuseIdentifier:(NSString *)identifier {
    self = [super initWithReuseIdentifier:identifier];
    if (self) {
        [self createLabel];
    }

    // Add the gesture recognizer.
    UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
    gestureRecognizer.delegate = self;
    [self addGestureRecognizer:gestureRecognizer];

    return self;
}

- (void)createLabel {
    // Create the two labels that are used to render the cell contents
    // NOTE: the background for each label is clear so that the border and background color of the cell
    // still shows through.
    _symbolLabel = [UILabel new];
    _symbolLabel.backgroundColor = [UIColor clearColor];
    _symbolLabel.textColor = [UIColor darkGrayColor];
    _symbolLabel.textAlignment = NSTextAlignmentLeft;

    _textLabel = [UILabel new];
    _textLabel.backgroundColor = [UIColor clearColor];
    _textLabel.textColor = [UIColor darkGrayColor];
    _textLabel.textAlignment = NSTextAlignmentLeft;

    [self updateLabels];
    
    [self addSubview:_symbolLabel];
    [self addSubview:_textLabel];
}

- (void)updateLabels {
	// Calculate the indent starting poing for the expand/collapse symbol and text label.
    CGFloat symbolX = 10 * _level;
    CGFloat textX = symbolX + 25;
    
    _symbolLabel.frame = CGRectMake(symbolX, 0, 20, self.bounds.size.height);
    _textLabel.frame = CGRectMake(textX, 0, self.bounds.size.width - textX, self.bounds.size.height);
}

- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];

    // When the cell frame is set, update the frame of the labels it contains.
    [self updateLabels];
}

-(void)handleTapGesture:(UITapGestureRecognizer *)gestureRecognizer
{
    if (!_hasChildren)
    {
		// Ignore if the row has no children.
        return;
    }
	
	// Toggle the expand/collapse state.
    self.isExpanded = !self.isExpanded;

    [_delegate didTapExpandCollapseCell:self];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // Make sure it recognizes any other gestures (I.E. the long press for row/column reordering).
    return YES;
}

@end



//
// ViewController.m
// DrillDownGrid
//
// Copyright (c) 2015 Wizgod Inc. All rights reserved.
//

#import "ViewController.h"
#import "WGExpandCollapseCell.h"

@interface ViewController () <SDataGridDelegate, WGExpandCollapseCellDelegate>

@property NSMutableArray *data, *filteredData;
@property ShinobiDataGrid *shinobiDataGrid;

@end

@implementation ViewController

@synthesize shinobiDataGrid, data, filteredData;

- (void)viewDidLoad {
    [super viewDidLoad];

    [self generateData];
	
	// Get all top level records.
    filteredData = [self getChildRecordsFromData:data byParentId:0];
    
    [self generateGrid];
}

- (void)generateData {
    data = [[NSMutableArray alloc] init];
	
	// The "hasChildren" below is set below automatically by looping through the records.
	// The "level" hierarchy is also set automatically by looping recursively through the data.
	//
	// This version of the drill down grid requires that the data have at least the "id" and "parentId" properties.
	// If one were to use an array of objects, these properties would need to be stored in another reference array/dictionary
	// and the code modified accordingly.
    [data addObject: [@{@"id" : @1, @"parentId" : @0, @"Row Labels" : @"2005", @"Internet Sales Amount" : @3266373.66, @"Reseller Sales Amount" : @8065435.31, @"KPI" : @11331808.96, @"KPI Goal" : @9513000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @2, @"parentId" : @0, @"Row Labels" : @"2006", @"Internet Sales Amount" : @6530343.53, @"Reseller Sales Amount" : @24144429.65, @"KPI" : @30674773.18, @"KPI Goal" : @29009000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @3, @"parentId" : @0, @"Row Labels" : @"2007", @"Internet Sales Amount" : @9791060.30, @"Reseller Sales Amount" : @32202669.43, @"KPI" : @41993729.72, @"KPI Goal" : @38782000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @4, @"parentId" : @0, @"Row Labels" : @"2008", @"Internet Sales Amount" : @9770899.74, @"Reseller Sales Amount" : @16038062.60, @"KPI" : @25808962.34, @"KPI Goal" : @18410000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @5, @"parentId" : @4, @"Row Labels" : @"2008 Q1", @"Internet Sales Amount" : @4283629.96, @"Reseller Sales Amount" : @7102685.11, @"KPI" : @11386315.07, @"KPI Goal" : @8051000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @6, @"parentId" : @5, @"Row Labels" : @"2008 Jan", @"Internet Sales Amount" : @1340244.95, @"Reseller Sales Amount" : @1662547.32, @"KPI" : @3002792.274, @"KPI Goal" : @8051000.00 } mutableCopy]];
    [data addObject: [@{@"id" : @7, @"parentId" : @5, @"Row Labels" : @"2008 Feb", @"Internet Sales Amount" : @1462479.83, @"Reseller Sales Amount" : @2700766.80, @"KPI" : @4163246.633, @"KPI Goal" : @0 } mutableCopy]];
    [data addObject: [@{@"id" : @8, @"parentId" : @5, @"Row Labels" : @"2008 Mar", @"Internet Sales Amount" : @1480905.18, @"Reseller Sales Amount" : @2739370.98, @"KPI" : @4220276.163, @"KPI Goal" : @0 } mutableCopy]];
    [data addObject: [@{@"id" : @9, @"parentId" : @4, @"Row Labels" : @"2008 Q2", @"Internet Sales Amount" : @5436429.15, @"Reseller Sales Amount" : @8935377.49, @"KPI" : @14371806.64, @"KPI Goal" : @10359000.00 } mutableCopy]];
	[data addObject: [@{@"id" : @10, @"parentId" : @4, @"Row Labels" : @"2008 Q3", @"Internet Sales Amount" : @50840.63, @"Reseller Sales Amount" : @0, @"KPI" : @50840.63, @"KPI Goal" : @0 } mutableCopy]];
	[data addObject: [@{@"id" : @11, @"parentId" : @0, @"Row Labels" : @"2010", @"Internet Sales Amount" : @0, @"Reseller Sales Amount" : @0, @"KPI" : @0, @"KPI Goal" : @0 } mutableCopy]];
	
    // Generate the hierarchy levels.
    [self setHierarchyForData:data parentId:0 level:1];
    
    // This isn't necessarly efficient but this is a prototype.
    for (NSMutableDictionary *record in data)
    {
        // Set the starting expand/collapse state.
        [record setValue:@NO forKey:@"isExpanded"];
    }
}

- (void)generateGrid {

// // Just an FYI
// UIView *scrollView = [[shinobiDataGrid subviews] objectAtIndex:0];
// UIView *gridView = [[scrollView subviews] objectAtIndex:0];
// UIView *headerView = [[gridView subviews] objectAtIndex:0];
// UIView *rowsAndColumnsMaybeScrollView = [[gridView subviews] objectAtIndex:1];
// UIView *rowsAndColumnsView = [[rowsAndColumnsMaybeScrollView subviews] objectAtIndex:0];
// UIView *firstColumnFirstCellView = [[rowsAndColumns subviews] objectAtIndex:3];

    shinobiDataGrid = [[ShinobiDataGrid alloc] initWithFrame:CGRectInset(self.view.bounds, 40,40)];

    shinobiDataGrid.backgroundColor = [UIColor lightGrayColor];

    SDataGridCellStyle *headerStyle = [[SDataGridCellStyle alloc] init];
    headerStyle.font = [UIFont fontWithName:@"Helvetica" size:14];
    headerStyle.backgroundColor = [UIColor darkGrayColor];
    headerStyle.textColor = [UIColor yellowColor];
    shinobiDataGrid.defaultCellStyleForHeaderRow = headerStyle;
    
    [self generateColumns];
    
    shinobiDataGrid.singleTapEventMask = SDataGridEventSelect;
    shinobiDataGrid.doubleTapEventMask = SDataGridEventNone;

    shinobiDataGrid.selectionMode = SGridSelectionModeRowSingle;
    
    shinobiDataGrid.dataSource = self;
    shinobiDataGrid.delegate = self;

    [self.view addSubview:shinobiDataGrid];
}

- (void)generateColumns {
    
    NSArray *columns = @[@"Row Labels", @"Internet Sales Amount", @"Reseller Sales Amount", @"KPI", @"KPI Goal"];
	
    NSUInteger numberOfColumns = columns.count;
    
    NSNumber *width = [NSNumber numberWithDouble:(self.view.bounds.size.width-80)/numberOfColumns];
    
    for (NSString *title in columns ) {
        // intantiate the SDataGridColumn
        SDataGridColumn* column = [[SDataGridColumn alloc] initWithTitle:title];
        column.width = width;
        // The first column will be the expand/collapse cell; all others will be the default cell type.
        if ([title isEqualToString:@"Row Labels"])
        {
            column.cellType = [WGExpandCollapseCell class];
        }
        
        [shinobiDataGrid addColumn:column];
    }
}

- (bool)setHierarchyForData:(NSMutableArray *)dataWithHierarchy parentId:(int)parentId level:(int)level {
    NSMutableArray *records = [self getChildRecordsFromData:dataWithHierarchy byParentId:parentId];
    
    if (records.count == 0) return false;
    
    for (NSMutableDictionary *record in records) {
        [record setValue:[NSNumber numberWithInt:level] forKey:@"level"];
        
        // Recursively set the hierarchy level for child records.
        bool hasChildren = [self setHierarchyForData:dataWithHierarchy parentId:[[record valueForKey:@"id"] intValue] level:level+1];
        
        // Set the hasChildren flag.
        [record setValue:hasChildren ? @YES : @NO forKey:@"hasChildren"];
    }
    
    return true;
}

- (NSMutableArray *)getChildRecordsFromData:(NSMutableArray *)dataToSearch byParentId:(int)recordId {
	return [[dataToSearch filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"parentId = %i", recordId]] mutableCopy];
}

- (NSMutableDictionary *)findRecordInData:(NSMutableArray *)dataToSearch byId:(int)recordId {
	NSArray *result = [dataToSearch filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"id = %i", recordId]];
	
	return result.count > 0 ? [result objectAtIndex:0] : nil;
}

- (int)addChildRecordsFromData:(NSMutableArray *)fromData toData:(NSMutableArray *)toData parentId:(int)parentId atIndex:(int)atIndex gridRowIndex:(int)gridRowIndex {
    NSMutableArray *childRecords = [self getChildRecordsFromData:data byParentId:parentId];
    
    if (childRecords.count == 0)
    {
        // There were no child records to display/insert.
        return atIndex;
    }

    for (int i = 0; i < childRecords.count; i++)
    {
        // Add the child records to our filtered data underneath the parent record.
        [filteredData insertObject:childRecords[i] atIndex:atIndex];
        
        // Increment the index to insert the next object at.
        atIndex++;
        
        // If this record has children, and it's state is expanded, insert those as well.
        if ([[childRecords[i] valueForKey:@"isExpanded"] boolValue] && [[childRecords[i] valueForKey:@"hasChildren"] boolValue]) {
            atIndex = [self addChildRecordsFromData:data toData:filteredData parentId:[[childRecords[i] valueForKey:@"id"] intValue] atIndex:atIndex gridRowIndex:gridRowIndex];
        }
    }
    
    // Uncomment this if you want to animate moving the rows down to make room for the inserted rows.
// NSArray *gridRows = [shinobiDataGrid visibleRows];
//    
// for (int i = gridRowIndex; i < gridRows.count; i++) {
// [self moveDataGridRow:gridRows[i] numberOfRows:(-1 * childRecords.count) rowWasDeleted:false];
// }

    return atIndex;
}

- (void)removeChildRecordsFromData:(NSMutableArray *)dataToFilter parentRow:(SDataGridRow *)parentRow parentLevel:(int)parentLevel {
    
    int parentIndex = parentRow.rowIndex;
    int numberOfRowsDeleted = 0;
    NSArray *gridRows = [shinobiDataGrid visibleRows];

    // As long as we don't go out of bounds and the next record is within the hierarchy of the parent, remove it.
	while (parentIndex + 1 < dataToFilter.count && [[dataToFilter[parentIndex + 1] valueForKey:@"level"] intValue] > parentLevel) {
        // Remove the record from the datasource.
        [dataToFilter removeObject:dataToFilter[parentIndex + 1]];

        // Increment the number of rows deleted.
        numberOfRowsDeleted++;
	}
    
    // Animate moving all rows up by the number of rows deleted.
    for (int i = parentIndex+1; i < gridRows.count; i++) {
        [self moveDataGridRow:gridRows[i] numberOfRows:numberOfRowsDeleted rowWasDeleted:(i - parentIndex) <= numberOfRowsDeleted];
    }
}

#pragma mark - ShinobiDataGridDataSource methods

- (NSUInteger)shinobiDataGrid:(ShinobiDataGrid *)grid numberOfRowsInSection:(NSInteger)sectionIndex {
    return self.filteredData.count;
}

- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell *)cell {
    NSString *title = cell.coordinate.column.title;
    NSMutableDictionary *rowData = filteredData[cell.coordinate.row.rowIndex];
    
    if ([title isEqualToString:@"Row Labels"]) {
		WGExpandCollapseCell* expandCollapseCell = (WGExpandCollapseCell*)cell;
		
		// The recordId and parentId are convenience properties and are not required by the custom cell
		// for displaying the indented state & text.
        expandCollapseCell.recordId = [[rowData valueForKey:@"id"] intValue];
        expandCollapseCell.parentId = [[rowData valueForKey:@"parentId"] intValue];
        expandCollapseCell.level = [[rowData valueForKey:@"level"] intValue];
        expandCollapseCell.hasChildren = [[rowData valueForKey:@"hasChildren"] boolValue];
        expandCollapseCell.isExpanded = [[rowData valueForKey:@"isExpanded"] boolValue];
		expandCollapseCell.textLabel.text = [rowData valueForKey:title];
		expandCollapseCell.delegate = self;
    } else if (![title isEqualToString:@"level"] && ![title isEqualToString:@"id"] && ![title isEqualToString:@"parentId"] && ![title isEqualToString:@"isExpanded"] && ![title isEqualToString:@"hasChildren"])
    {
        NSString *value = [[rowData valueForKey:title] stringValue];
        
        if (![title isEqualToString:@"KPI"])
        {
            NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
            [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
            value = [formatter stringFromNumber:[rowData valueForKey:title]];
        }
        
        SDataGridTextCell* textCell = (SDataGridTextCell*)cell;
        textCell.textField.text = value;
    }
}

- (void)moveDataGridRow:(SDataGridRow *)row numberOfRows:(int)numberOfRows rowWasDeleted:(bool)rowWasDeleted {
    NSArray *visibleColumns = [shinobiDataGrid visibleColumns];

    for (int i = 0; i < visibleColumns.count; i++) {
        SDataGridCoord *coord = [SDataGridCoord coordinateWithCol:visibleColumns[i] row:row];
        SDataGridCell *cell = [shinobiDataGrid visibleCellAtCoordinate:coord];

        if (rowWasDeleted)
        {
            // If the row is one that was deleted, place that view behind the grid so it becomes hidden when moving up over and/or above the parent row.
            [cell.superview sendSubviewToBack:cell];
        }

        [self moveViewWithAnimation:cell endX:cell.frame.origin.x endY:cell.frame.origin.y - (cell.frame.size.height * numberOfRows) duration:0.2 bounce:0];
    }
}

- (void) didTapExpandCollapseCell:(WGExpandCollapseCell *)cell {
    
    NSString *title = cell.coordinate.column.title;
    SDataGridRow *row = cell.coordinate.row;
    
    // Set the expanded value in the filtered data.
    NSMutableDictionary *rowData = filteredData[cell.coordinate.row.rowIndex];
	
	// Set the current expanded state in the data object of the selected row.
    [rowData setValue:cell.isExpanded ? @YES : @NO forKey:@"isExpanded"];
	
    if ([title isEqualToString:@"Row Labels"])
    {
        [CATransaction begin]; {
            [CATransaction setCompletionBlock:^{
                [shinobiDataGrid reload];
            }];

            if (cell.isExpanded)
            {
                // Add all child records of the parent record and subsequent child records in the hierarchy that are also flagged as expanded.
                [self addChildRecordsFromData:data toData:filteredData parentId:cell.recordId atIndex:cell.coordinate.row.rowIndex+1 gridRowIndex:cell.coordinate.row.rowIndex+1];
            } else {
                // Remove all records in the hierarchy of the selected parent record.
                [self removeChildRecordsFromData:filteredData parentRow:row parentLevel:cell.level];
            }
        } [CATransaction commit];
    }
}

- (void)moveViewWithAnimation:(UIView *)view endX:(CGFloat)endX endY:(CGFloat)endY duration:(float)duration bounce:(float)bounce
{
    // Move view to this location.
    CGRect endFrame = CGRectMake(endX, endY, view.frame.size.width, view.frame.size.height);

    if (bounce == 0) {
        [UIView animateWithDuration:duration
                         animations:^{
                                view.frame = endFrame;
                            }
                         completion:nil];
    } else {
        [UIView animateWithDuration:duration
                              delay:0
             usingSpringWithDamping:bounce
              initialSpringVelocity:0.0
                            options:0 animations:^{
                                view.frame = endFrame;
                                }
                         completion:nil];
    }
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    
    // Set the grid frame and column sizes for the new orientation.
    shinobiDataGrid.frame = CGRectInset(self.view.bounds, 40,40);
    NSNumber *width = [NSNumber numberWithDouble:shinobiDataGrid.frame.size.width/shinobiDataGrid.columns.count];
    
    for (SDataGridColumn *column in shinobiDataGrid.columns)
    {
        column.width = width;
    }
    
    // Reload the changes and data.
    [shinobiDataGrid reload];

    // You could also remove the grid and re-add it but you would essentially be creating a new instance whereas
    // you're only changing the frame and width sizes above.
    //[shinobiDataGrid removeFromSuperview];
    //[self generateGrid];
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

@end

#6

Added:

Generation of hierarchy level from id/parentId; the data only requires these two properties now.

Restoration of expanded/collapsed state of child records when expanding parent.

Set the “hasChildren” property at the same time as generating the hierarchy level.

Added animation of removing and adding records.

Wg


#7

nice solution wizgod…  :grin: but when i run this in stumulator(landscape mode) grid didn’t fill ipad horizontal space.but if I rotate stumulator to portrait position and again rotate it to landscape position grid fill ipad horizontal space… why is that…??


#8

I assume you were able to get your JSON data sorted out, eh?

I ran it on the iPad 2 (8.1) simulator and it worked fine when starting in landscape mode.

I also changed the didRotateFromInterfaceOrientation method to simply change the frame and column sizes (instead of removing and recreating the datagrid) and it also worked fine.

Wg


#9

:grin:yap.that’s fine wizgod…ah…I run it on iPad(7.0)…i changed didRotateFromInterfaceOrientation method also…but problem still there… :confused:… problem might be in simulator no…??? so I gave fixed width for column…then it works fine… :wink:


#10

It could be.

I don’t have the iPad (7.0) simulator available to me; only iPad 2, Air, Retina and Resizable. Running on these simulators, the orientation change, with the column width calculated, works fine (and also on my physical iPad running 8.1). I tested using both 8.1 and 7.1 as the deployment targets.

Wg


#11

that is the problem wizgod…! it works fine with Retina…

for my grid I need to add custom cell(for example KPI cell) like this…

KPI
     
      ▲ 22%
      ▼ 15%
      ▲ 45%
      ▼ 7%
      ▼ 13%

this is KPI column… in here data sent through json file  (for ▲ symbol “movement”  :1 , for ▼ symbol “movement” :0, and “percentage”: 0.22 ) . movement and percentage values are two datapoins that include in each dictionary.

[data addObject: [@{ @"id" : @1, 
                               @"parentId" : @0,
                               @"Row Labels" : @"2005",
                               @"Internet Sales Amount" : @3266373.66, 
                               @"Reseller Sales Amount" : @8065435.31, 
                               @"percentage" : @0.22,
                               @"movement" : @1,
                               @"KPI Goal" : @9513000.00 } mutableCopy]];

 what is the best way to add cell like that…??


#12

The PriceCell in the CreatingCustomCell sample app is pretty much what you want to do. Just modify that custom cell for handling your values.

Wg


#13

Hi… I added custom cell… it works fine and data was added correctly… But when I expand one row or scroll down some views of custom cell is missing… then I scroll down table those missing cells will apper… this happons only custom cell… is there any solution for this…??


#14

hey… I fixed it. :sunglasses: problem was I didn’t add subView correctly… :wink: