IGGridView : Using a custom datasource helper to swap rows and columns (Objective C / Xamarin.iOS).

Darrell Kress / Monday, January 13, 2014

Updated on 17 March 2014 to include Xamarin iOS sample.



Introduction 

We recently had a question in the forums about using the IGGridView to show columns as rows and rows as columns which can be accomplished using a custom IGGridViewDataSourceHelper.

IGGridViewDataSourceHelper Customization


So let's hop right in.  The default datasource helper renders what we could consider a common row / column grid.    But we want the rows and columns to be flipped. 

The first part of that process is overriding the informative information that the datasource helper is going to provide the IGGridViewControl. 

// the number of columns in this case will be the number of rows for a normal grid (since we are flipping the two).
-(NSInteger)numberOfColumnsInGridView:(IGGridView *)gridView
{
  return [super gridView:gridView numberOfRowsInSection:0];
}
// And similarly the number of rows in this case will be the number of columns in a normal grid
-(NSInteger)gridView:(IGGridView *)gridView numberOfRowsInSection:(NSInteger)section
{
return [super numberOfColumnsInGridView:gridView];
}
// Since we are flipping the columns and the rows when we want this method call to flip them for us
-(IGCellPath *)normalizePath:(IGCellPath *)path {
return [IGCellPath pathForRow:path.columnIndex inSection:path.sectionIndex inColumn:path.rowIndex];
}
// And this reverses the change made in normalizePath.
-(IGCellPath *)deNormalizePath:(IGCellPath *)path
{
return [IGCellPath pathForRow:path.columnIndex inSection:path.sectionIndex inColumn:path.rowIndex];
}

The first two methods : numberOfColumnsInGridView:(IGGridView *)gridView and numberOfRowsInSection:(NSInteger)section will tell the IGGridView how many columns and rows will appear. So we reverse the call slightly . For the number of rows we give the expected number of columns, for the number of columns we give the number of rows. The trickiest part in that code is the section number. Sections are groups of data, a group by type feature. Since this example isn't grouping, everything is going to be in section zero.

So the other two methods help map data between the underlying data array and how the cells and rows  were displayed.  In a normal rendering, a single data object is in its own row.  Here we have to accommodate that a data object is now rendering up and down.

So now the datasource helper will tell the view it's row and column layout information.  Pretty easy.

Next we will have the view add a "HeaderRow" and the column headers which will lay out our view area.  

We want to give the view a column full of row headers.  We want that to always be visible.  So we can use a FixedColumn.  

// We will use the fixed left column to hold the column headers now moved to be row headers.  So mark a single column as fixed.

-(NSInteger)numberOfFixedLeftColumnsInGridView:(IGGridView *)gridView

{

    return 1;

}

// When the fixed column cells are being rendered, we will want to take control of that process to show the row headers for the row.  We will put the column header text into the cell.

-(IGGridViewCell *)gridView:(IGGridView *)gridView fixedLeftCellAt:(IGCellPath *)path

{

    IGGridViewCell* c = [gridView dequeueReusableCellWithIdentifier:@"fc"];

    if (!c)

    {

        c= [[IGGridViewCell alloc]initWithReuseIdentifier:@"fc"];

    }

    NSString* columnHeader = ((IGGridViewColumnDefinition*) self.columns[path.rowIndex]).headerText;

    c.textLabel.text = columnHeader;

    c.backgroundColor = [UIColor greenColor];

    return c;

}

Since there is only a single fixed column in this layout, we can hard set the data source helper to return a single fixed column. We then grab the Column.headerText for the column header that will be displayed and render that in our fixed column.

After that, we will render a custom column header cell.  And that code is easy as well.

// We created a custom headerCell to show the image of the person whose data is being displayed.  Override this method to get the data

-(IGGridViewHeaderCell *)gridView:(IGGridView *)gridView headerCellAt:(NSInteger)column

{

    MyCustomHeaderCellWithImageCell* hc = [gridView dequeueReusableCellWithIdentifier:@"hc"];

    if(!hc)

    {

        hc =[[MyCustomHeaderCellWithImageCell alloc]initWithReuseIdentifier:@"hc"];

    }

    // Gets the "real" row of data from the data source

    IGCellPath* path = [IGCellPath pathForRow:0 inSection:0 inColumn:column];

    path = [self deNormalizePath:path];

    

    // then gets the data object

    igSalesmanItem* item = self.data[path.rowIndex];

    

    // and finds the image associated with that data object.

    hc.image = item.image;

    

    return hc;

}

And that's it for the datasource helper. In the sample code we override another method or two, but those are cosmetic. We also define a custom header cell, but that is so we can show and image. We now have a datasource helper that will swap columns and rows. That is the important part.

So how do we use it? Well just like any datasource helper.

- (void)viewDidLoad

{

    [super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

    grid = [[IGGridView alloc]init];

    grid.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;

    grid.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);

    grid.columnWidth = [IGColumnWidth CreateWithFixedWidth:150];

    grid.headerHeight = 150;

    [self.view addSubview:grid];

    

    helper = [[RowColumnReversedDataSourceHelper alloc]init];

    

    helper.autoGenerateColumns = false;

    

    IGGridViewColumnDefinition *firstNameColumn = [[IGGridViewColumnDefinition alloc]initWithKey:@"firstName"];

    

    IGGridViewColumnDefinition *lastNameColumn = [[IGGridViewColumnDefinition alloc]initWithKey:@"lastName"];

    

        IGGridViewColumnDefinition *territoryColumn = [[IGGridViewColumnDefinition alloc]initWithKey:@"territory"];

    

    IGGridViewColumnDefinition *yearToDateColumn = [[IGGridViewColumnDefinition alloc]initWithKey:@"yearToDateSales"];

    

    [helper.columnDefinitions addObject:firstNameColumn];

    [helper.columnDefinitions addObject:lastNameColumn];

    [helper.columnDefinitions addObject:territoryColumn];

    [helper.columnDefinitions addObject:yearToDateColumn];

    

    

    NSMutableArray* myData = (NSMutableArray*)[igSalesmanItem generateData:10];

    helper.data = myData;

    grid.dataSource = helper;

}

We define some column definitions, add them to the DataSourceHelper so we can limit the columns we want, hook it up to some data, and give it to a igGridView to render.

So I hope you enjoyed learning a bit about how to customize your datasource helper and how it can be used to make different UIs for your application.

Attached to this article is a sample showing this datasource helper in action in Objective-C and in Xamarin-iOS.  

Sample in Objective-C

Sample in Xamarin-iOS

By Darrell Kress

http://www.infragistics.com/community/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/baldnbearded/2018.CustomDataSource.zip