UITableView Part 2 – DataSources and Sources

In every programming language and operating system, there is always a grid control, which needs data. How the grid gets it data varies slightly; however, conceptually the grid gets the data through a data source. Sometimes the data source is the raw data itself, and sometimes it’s a wrapper around the data, which the wrapper offers some additional functionality. In iOS, there are two ways to bind the UITableView, through a UITableViewDataSource object and a UITableViewSource object. Both forementioned classes, similar in nature, are delegate classes that act as an intermediary between the underlying data and the tableview. In iOS, a delegate is a construct much different from the class that drives the behavior of the tableview. In Xamarin iOS, the UITableViewSource is a class with a bunch of optional methods. Overriding particular methods allow a developer to tap into certain functionality of and receive feedback from the tableview.

This post is going to discuss how to bind to a UITableView, and assumes you are familiar with the basic features of iOS and the UITableView. To begin, in order to use a UITableViewSource for a TableView in Xamarin iOS, follow these steps.

First, create a new class that inherits from UITableViewSource:

public class MySource : UITableViewSource

Next, override the following method:

UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)

The GetCell method is responsible for creating and populating a UITableViewCell that the UI will return to the current user, and may look like the following implementation:

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
	var cell = tableView.DequeueReusableCell ("StringIdentifier");
	if (cell == null)
		cell = new UITableViewCell(UITableViewCellStyle.Default, "StringIdentifier");

	//Load data into cell from data source

	return cell;
}

First we see if a cell has already been created for this identifier. The iOS framework will try to reuse an existing cell if it’s already created. If it isn’t created, then the cell is instantiated and returned. Additionally, the RowsInSection method is used to determine the number of rows in the current section. RowsInSection is useful for grouping of tables (which is supplied in the constructor of the UITableViewController, but that’s for another discussion). When the table is grouped, the RowsInSection method needs to return the number of rows (in integer form) for the given groups at the specified index path (since the NSIndexPath contains the current section and the current data item pointers).

The GetCell and RowsInSection methods are the most critical methods because it controls how the data is loaded in the UI.

There are plenty of other useful methods to know about to. For instance, if you want to handle the click of a row, handle the RowSelected event:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    //DO something

    tableView.DeselectRow (indexPath, true); // iOS convention is to remove the highlight
}

The UITableViewSource has methods for overriding the height of the row, and header/footer (if using grouping), as well as change the current view used for the header and footer. For instance, the view could be changed to use an image as the header and footer, as I wrote about before.

Note that the UITableViewSource class doesn’t retain your data source; you need to store the data in a variable so it can be retrieved through the index path later. Also, note that the source needs to be created for every controller you may want to use it in, leading to a lot of repetition.

Now I’m not one for repetition; I really don’t like the idea of writing boilerplate code just for the sake of writing boilerplate code. Therefore, I decided to create a reusable base class that abstracts most of the functionality away. It takes away a lot of the repetitiveness, while also offering the full functionality. To use it, all you need to do is inherit from Nucleo.UI.BaseUITableViewSource, and override the GetCellIdentifier and PopulateCell methods, and set the Data or GroupedData properties (depending on whether the table is grouped or not). This means you really could define a UITableViewSource like:

public class MySource : Nucleo.UI.BaseUITableViewSource
{
    public override string GetCellIdentifier() { return "My"; }

    public override MyClass PopulateCell(UITableViewCell cell, T dataItem, NSIndexPath path)
    {
        //Cell is already dequeued or created, only populate the cell here
    }
}

The process of creating the cell is abstracted away for you; however, if you are using a custom cell (more on this later), then you can override the CreateNewCell class and return your custom cell, and cast the cell reference in the PopulateCell method. Additionally, all of the grouping functionality is handled for you automatically by setting GroupedData property; RowsInSection and SectionFor methods, all used for grouping, are already wired up.

If you want to view the full code sample, you can do so from here. You can also find my project where I have this code at my Nucleo Mobile bitbucket.org repository.

Advertisements

One thought on “UITableView Part 2 – DataSources and Sources

  1. Pingback: UITableViews Part 3: Showing a View Over a UITableViewCell in Xamarin iOS | On All Things Web

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s