New-age Transactional Systems - Not Your Grandpa's OLTP
John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.
The content has been bookmarked!
There was an error bookmarking this content! Please retry.

Posted by Bryan Costanich on Apr 01, 2010
The source code for the completed example can be found here (for those that want to examine the code without running through the steps, or if you want to compare).
Tables are a staple in many iPhone applications. Although they’re certainly not a new invention by any means in terms of application development, tables in the iPhone are very specific, given the constraints of the size of the device.
Out of the box, Apple gives you quite a bit of ability to customize your tables through styles. When you first create a table, however, it looks pretty basic. Without any real customizations, there are two basic styles you get for your tables, the default style and the grouped style:

With a few tweaks to the table cells within the table, you can add images and accessories to each table cell as in:

You can even change the font and colors of the table cells, however, sometimes this just isn’t enough. If you truly want to break out of the box and create complex UIs, you have to create your own custom table cells. The following screen shot is an example of just that, an application that uses completely custom table cells to display content:

Fortunately for us, Apple gives us a pretty easy way to do this by allowing us to create our own custom UITableViewCell controls and use them in our UITableView controls. Let’s walk through creating an application that explores working with the UITableView and UITableViewCell controls to create the preceding examples.
So let’s create the application. First, open up MonoTouch, it should look something like the following:
Next, create a new iPhone MonoTouch project. To do this, select File : New Solution:

Choose “iPhone Window-based Project” from the C# iPhone templates and let’s name our solution “Example_CustomUITableViewCells”:
Click “OK” on the Project Features dialogue, as they’re not relevant to what we’re doing:
Our project should now look something like this:

Open the MainWindow.xib file in Interface Builder by double-clicking on it in the Solution Explorer window, you should get something that looks like this:
Let’s start by dragging a UITableView control onto our MainWindow, it should now look like:

We need to add an outlet on our AppDelegate class, so that we can access this Table View programmatically.
To do this, in the Library window, select the “Classes” tab at the top, and then “Other Classes” in the drop-down. Then, make sure AppDelegate is selected, and click the “Outlets” tab in the second half of the Library window:

Let’s create an outlet called tblMain, by clicking the “+” button and typing in “tblMain” for the name, your outlets should now look like:

Next, let’s wire up the outlet to the UITableView we added. Select your AppDelegate in the document window, then in the Connections Inspector window, you should see the newly created tblMain outlet:
Wire this outlet up to the our table by dragging from the circle to the right of the outlet in the connections manager to our table in the Document window, or in the Window Designer:
Now that we got our table wired up, let’s switch back over to MonoDevelop and write some code.
Right-click on your project and choose “Add,” and then “New Folder”. Name the folder “Code.”
Next, a class named “BasicTableViewItem.cs.” To do this, right-click on the project again and choose “Add,” and then “New File.” Name the class and click “New:”

In the file, put the following code:
namespace Example_CustomUITableViewCells
{
//========================================================================
/// <summary>
/// Represents our item in the table
/// </summary>
public class BasicTableViewItem
{
public string Name { get; set; }
public string SubHeading { get; set; }
public string ImageName { get; set; }
public BasicTableViewItem ()
{
}
}
//========================================================================
}
This class is fairly simple, it represents an item in a cell and has the following properties:
We’ll see how we use this class later.
Next, create another class in the “Code” folder, name it “BasicTableViewItemGroup,” and put the following code in it:
using System;
using System.Collections.Generic;
namespace Example_CustomUITableViewCells
{
//========================================================================
/// <summary>
/// A group that contains table items
/// </summary>
public class BasicTableViewItemGroup
{
public string Name { get; set; }
public string Footer { get; set; }
public List<BasicTableViewItem> Items
{
get { return this._items; }
set { this._items = value; }
}
protected List<BasicTableViewItem> _items = new List<BasicTableViewItem>();
public BasicTableViewItemGroup ()
{
}
}
//========================================================================
}
This is also a fairly basic class. It serves as a container for groups of items. It has the following properties:
Next, create another class in the same folder, name it “BasicTableViewSource,” and add the following code to it:
using System;
using System.Collections.Generic;
using MonoTouch.UIKit;
namespace Example_CustomUITableViewCells
{
//========================================================================
/// <summary>
/// Combined DataSource and Delegate for our UITableView
/// </summary>
public class BasicTableViewSource : UITableViewSource
{
//---- declare vars
protected List<BasicTableViewItemGroup> _tableItems;
string _cellIdentifier = "BasicTableViewCell";
public BasicTableViewSource (List<BasicTableViewItemGroup> items)
{
this._tableItems = items;
}
/// <summary>
/// Called by the TableView to determine how many sections(groups) there are.
/// </summary>
public override int NumberOfSections (UITableView tableView)
{
return this._tableItems.Count;
}
/// <summary>
/// Called by the TableView to determine how many cells to create for that particular section.
/// </summary>
public override int RowsInSection (UITableView tableview, int section)
{
return this._tableItems[section].Items.Count;
}
/// <summary>
/// Called by the TableView to retrieve the header text for the particular section(group)
/// </summary>
public override string TitleForHeader (UITableView tableView, int section)
{
return this._tableItems[section].Name;
}
/// <summary>
/// Called by the TableView to retrieve the footer text for the particular section(group)
/// </summary>
public override string TitleForFooter (UITableView tableView, int section)
{
return this._tableItems[section].Footer;
}
/// <summary>
/// Called by the TableView to get the actual UITableViewCell to render for the particular section and row
/// </summary>
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
//---- declare vars
UITableViewCell cell = tableView.DequeueReusableCell (this._cellIdentifier);
//---- if there are no cells to reuse, create a new one
if (cell == null)
{
cell = new UITableViewCell (UITableViewCellStyle.Default, this._cellIdentifier);
}
//---- create a shortcut to our item
BasicTableViewItem item = this._tableItems[indexPath.Section].Items[indexPath.Row];
cell.TextLabel.Text = item.Name;
return cell;
}
}
//========================================================================
}
The UITableViewSource class is responsible for handling the data binding of our data to our table and also handling the user interaction with the table. In our case, handling user interaction is out of the scope of this article, but we’re still doing our data binding.
This class is a little more complicated than the other two, but let’s break it down:
Ok, now that we’ve got those classes out of the way, we’re going to add some images so we’ve got something to display with the items.
First, let’s create a folder in the project called “Images.” Do this the same way you created the “Code” folder from before. Save the following images to your hard drive with the following names, respectively:
|
Image |
Name |
|
|
PawIcon.png |
|
|
LightBulbIcon.png |
|
|
PushPinIcon.png |
|
|
TargetIcon.png |
Once you have those saved, let’s add them to your project. Right-click on your “Images” folder and choose “Add Files.” Select the images that you saved, and check “Override default build action,” and select “Content” in the drop-down. This is very important. Content is the only build action that will allow you to load them and use them at run time.
Your solution should now look like this:

Make sure and build your project to make sure you’ve done everything correctly so far. To build, hold down the Apple Key and press the “B” button, or choose “Build All” from the Build menu.
All right, now that we have our classes and our images, let’s go edit our application code to use them!
Double click on Main.cs to bring the code editor up. In your AppDelegate class, add the following line:
BasicTableViewSource _tableViewSource;
This is a class variable to hold our BasicTableViewSource that we created earlier.
Next, put the following method in your AppDelegate class:
/// <summary>
/// Creates a set of table items.
/// </summary>
protected void CreateTableItems ()
{
List<BasicTableViewItemGroup> tableItems = new List<BasicTableViewItemGroup> ();
//---- declare vars
BasicTableViewItemGroup tGroup;
//---- birds
tGroup = new BasicTableViewItemGroup() { Name = "Birds", Footer = "Birds have wings, and sometimes use them." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Crow", SubHeading = "AKA, Raven.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Chicken", SubHeading = "Males are called roosters.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Turkey", SubHeading = "Eaten at thanksgiving.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- fish
tGroup = new BasicTableViewItemGroup() { Name = "Fish", Footer = "Fish live in water. Mostly." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Trout", SubHeading = "Rainbow is a popular kind.", ImageName = "TargetIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Salmon", SubHeading = "Good sushi.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Cod", SubHeading = "Flat fish.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- mammals
tGroup = new BasicTableViewItemGroup() { Name = "Mammals", Footer = "Mammals nurse their young." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Deer", SubHeading = "Bambi.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Bats", SubHeading = "Fly at night.", ImageName = "TargetIcon.png" });
tableItems.Add (tGroup);
this._tableViewSource = new BasicTableViewSource(tableItems);
}
We’ll call this method to create our data source complete with some data to display in our table.
Finally, in your FinishedLaunching method of your AppDelegate class, add the following code before window.MakeKeyAndVisible is called.
this.CreateTableItems (); this.tblMain.Source = this._tableViewSource;
This calls the method that we just added to create our table data source, and then assign that data source to our table.
Your finished Main.cs should now look like:
using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace Example_CustomUITableViewCells
{
public class Application
{
static void Main (string[] args)
{
UIApplication.Main (args);
}
}
// The name AppDelegate is referenced in the MainWindow.xib file.
public partial class AppDelegate : UIApplicationDelegate
{
BasicTableViewSource _tableViewSource;
// This method is invoked when the application has loaded its UI and its ready to run
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
this.CreateTableItems ();
this.tblMain.Source = this._tableViewSource;
// If you have defined a view, add it here:
// window.AddSubview (navigationController.View);
window.MakeKeyAndVisible ();
return true;
}
// This method is required in iPhoneOS 3.0
public override void OnActivated (UIApplication application)
{
}
/// <summary>
/// Creates a set of table items.
/// </summary>
protected void CreateTableItems ()
{
List<BasicTableViewItemGroup> tableItems = new List<BasicTableViewItemGroup> ();
//---- declare vars
BasicTableViewItemGroup tGroup;
//---- birds
tGroup = new BasicTableViewItemGroup() { Name = "Birds", Footer = "Birds have wings, and sometimes use them." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Crow", SubHeading = "AKA, Raven.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Chicken", SubHeading = "Males are called roosters.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Turkey", SubHeading = "Eaten at thanksgiving.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- fish
tGroup = new BasicTableViewItemGroup() { Name = "Fish", Footer = "Fish live in water. Mostly." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Trout", SubHeading = "Rainbow is a popular kind.", ImageName = "TargetIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Salmon", SubHeading = "Good sushi.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Cod", SubHeading = "Flat fish.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- mammals
tGroup = new BasicTableViewItemGroup() { Name = "Mammals", Footer = "Mammals nurse their young." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Deer", SubHeading = "Bambi.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Bats", SubHeading = "Fly at night.", ImageName = "TargetIcon.png" });
tableItems.Add (tGroup);
this._tableViewSource = new BasicTableViewSource(tableItems);
}
}
}
Let’s run the app and see what it looks like. To run the app, hold down the Apple key and press the “Enter” button, or “Debug” from the Run menu.
When we run it, we should see:

The footer looks a little weird in this mode. Let’s change the table type to Grouped and re-run it. To do this, let’s open the MainWindow.xib back up in Interface Builder (by double-clicking it), and edit the table’s style. Click on the Table View in the Document window, and then in the Inspector window, change the style drop-down to “Grouped:”
Save the file, switch back to MonoDevelop and run the application again, you should now see:

The footers now make a little more sense.
Let’s play with some other stuff. We, have an image and a sub-heading on our item, so let’s utilize them.
Stop the application and then double-click our BasicTableViewSource.cs class to edit it. Change this line, in our GetCell method:
cell = new UITableViewCell (UITableViewCellStyle.Default, this._cellIdentifier);
to this:
cell = new UITableViewCell (UITableViewCellStyle.Subtitle, this._cellIdentifier);
This changes the style of the UITableViewCell to the Subtitle style, which displays a second line of text within the cell.
There are a few styles that you can play with. Note, however, using images is only supported in the Default and Subtitle styles.
Now, just after this line:
cell.TextLabel.Text = item.Name;
add:
cell.Accessory = UITableViewCellAccessory.Checkmark;
cell.DetailTextLabel.Text = item.SubHeading;
if(!string.IsNullOrEmpty(item.ImageName))
{
cell.ImageView.Image = UIImage.FromFile("Images/" + item.ImageName );
}
The out of the box UITableViewCells are laid out as follows:

There are two main parts to a UITableViewCell control, out of the box, the cell body, which is the entire area that the cell takes up, and the cell content, which is what we generally are allowed to put content in. If an accessory is not used, the cell content expands to take up the entire cell body. Similarily, if an image is not used, the label area overtakes the image are.
Let’s look at how we use this in our code. The first line tells the cell to have a Checkmark accessory in the right portion of the cell. Then we add our SubHeading text to the DetailTextLabel. Finally, we set an image on the cell.
It’s easy to create an image from our files. We simply use the UIImage.FromFile constructor, and pass it the path to our image. By setting our build type to Content on the images earlier, we can access them as if our folder structure in the project was our file system.
Running the application now, should get us the following:

Pretty cool. If we switch the table style in Interface Builder back to Plain from Grouped (open MainWindow.xib up by double-clicking, select our table, and change the style to “Plain” in the drop-down, and save), and run it again, we get:

Again pretty cool.
Since the 3.0 release of the iPhone OS, we’ve also been able to customize a number of other things including TextColor, SelectedTextColor, SelectedImage, BackgroundView, SelectedBackgroundView, and others. These give you a number of possibilities, but sometimes we want something radically different.
In order to accomplish this, we have to create our own table cell altogether. Apple allows us do this by inheriting from UITableViewCell and creating our own content, any way we want.
So let’s do just that. First, create a new folder in your project called “Controls.” In it, we’re going to create a new View with View Controller. Right-click on the Controls folder and select “Add,” and then “New File.” Choose “iPhone” from the left-pane and “View Interface Definition with Controller” on the right and name it “CustomTableViewCell:”
Open your CustomTableViewCell.xib file in Interface Builder by double-clicking it. It should be a blank view:
We don’t want a standard view, we want to create a UITableViewCell, so we’re going to delete the view and replace it with a cell.
Delete the View from your Document window by holding down the Apple key and press the Backspace key, or choose “Delete” from the edit window.
Next, drag a Table View Cell from the Library window onto your Document:
If you double-click on your newly added table cell, it’ll open up in the designer, and you can see it gives us a surface on which to add controls:
Let’s use Interface Builder to design our custom cell now. Make it a little taller by dragging the resize handle in the bottom right. Then, from the Library window, drag on a UIImageView and a couple of UILabels:

You can use the Attributes Inspector window to change the size of the UILabel controls, as well as the color, etc. It doesn’t have to look exactly like this, just as long as the elements are there.
Now that we have our custom design, let’s add outlets for our custom cell and it’s contents on the File’s Owner so that we can access them. Before, we added outlets directly to the App Delegate, but typically, we add them to File’s Owner, so that they get created on the class that owns the view.
To do this, select “File’s Owner” in the document window, then in the Library window, select the “Classes” tab at the top, and the “Other Classes” item in the drop-down. Select our CustomTableViewCell class, and then select the “Outlets” tab in the second half of the window. Add Outlets called “cellMain,” “lblHeading,” “lblSubHeading,” and “imgMain:”
Wire these outlets up to the controls that you previously created, just like you did before with our table.
Make sure “File’s Owner” is selected in the Document window and drag your control from the Connection Inspector to the control in the Document window:
The last thing we have to do in Interface Builder is to provide a cell identifier. Click on “Table View Cell” in the Document window, then in the Attributes Inspector, set the Identifier property to “MyCustomTableCellView:”
This allows our custom cell template to be reused, as we did before in code.
We’re done in Interface Builder, so save your file and then go back to MonoDevelop. Double-click on CustomTableCellView.xib.cs to open the class file.
We’re going to modify this class a little bit. First, let’s add the following properties to the class:
public UITableViewCell Cell
{
get { return this.cellMain; }
}
public string Heading
{
get { return this.lblHeading.Text; }
set { this.lblHeading.Text = value; }
}
public string SubHeading
{
get { return this.lblSubHeading.Text; }
set { this.lblSubHeading.Text = value; }
}
public UIImage Image
{
get { return this.imgMain.Image; }
set { this.imgMain.Image = value; }
}
Let’s take a look at these properties:
Next, we need to modify one of the constructors. The last constructor in file looks like this, when the file is created by MonoDevelop:
public CustomTableViewCell () : base("CustomTableViewCell", null)
{
Initialize ();
}
This seems a fairly straightforward constructor, if you read the Mono documentation on the base constructor that it’s calling (found here) it says that it initializes the class and loads the Nib (compiled .xib file) with the the name that we passed in. What the documentation fails to mention however, is that the base class constructor is asynchronous. If we use this call as is, the Nib will likely not actually get loaded immediately, and when we go to set our properties such as Heading, on our custom cell, we’ll get the dreaded null-reference error.
In order to fix that, we have to make sure the Nib file gets loaded synchronously, and the method doesn’t return until it has been loaded.
To do this, modify your constructor so that it looks like this.
public CustomTableViewCell ()// : base("CustomTableViewCell", null)
{
//---- this next line forces the loading of the xib file to be synchronous
MonoTouch.Foundation.NSBundle.MainBundle.LoadNib ("CustomTableViewCell", this, null);
Initialize ();
}
The first thing that we’re doing here is to comment out the call to the base-class constructor. We’re going to manually make this call, so we don’t need an extra one here. Next, we added a call to the LoadNib method. This does the exact same thing that our base-class constructor did, except that it forces it to be synchronous. Now, we’re assured that the Nib, and everything in it, will be initialized so that we can access it.
Our completed class should now look something like the following:
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System;
namespace Example_CustomUITableViewCells
{
//========================================================================
public partial class CustomTableViewCell : UIViewController
{
#region Constructors
// The IntPtr and initWithCoder constructors are required for controllers that need
// to be able to be created from a xib rather than from managed code
public CustomTableViewCell (IntPtr handle) : base(handle)
{
Initialize ();
}
[Export("initWithCoder:")]
public CustomTableViewCell (NSCoder coder) : base(coder)
{
Initialize ();
}
public CustomTableViewCell ()// : base("CustomTableViewCell", null)
{
//---- this next line forces the loading of the xib file to be synchronous
MonoTouch.Foundation.NSBundle.MainBundle.LoadNib ("CustomTableViewCell", this, null);
Initialize ();
}
void Initialize ()
{
}
#endregion
public UITableViewCell Cell
{
get { return this.cellMain; }
}
public string Heading
{
get { return this.lblHeading.Text; }
set { this.lblHeading.Text = value; }
}
public string SubHeading
{
get { return this.lblSubHeading.Text; }
set { this.lblSubHeading.Text = value; }
}
public UIImage Image
{
get { return this.imgMain.Image; }
set { this.imgMain.Image = value; }
}
}
//========================================================================
}
Ok, now that we have our custom cell class all figured out, let’s go and build a custom UITableViewSource class for it.
Create a new class in the “Code” folder, and copy the following into it:
using System;
using System.Collections.Generic;
using MonoTouch.UIKit;
namespace Example_CustomUITableViewCells
{
//========================================================================
/// <summary>
/// Combined DataSource and Delegate for our UITableView with custom cells
/// </summary>
public class CustomTableViewSource : UITableViewSource
{
//---- declare vars
protected List<BasicTableViewItemGroup> _tableItems;
protected string _customCellIdentifier = "MyCustomTableCellView";
protected Dictionary<int, CustomTableViewCell> _cellControllers =
new Dictionary<int, CustomTableViewCell>();
public CustomTableViewSource (List<BasicTableViewItemGroup> items)
{
this._tableItems = items;
}
/// <summary>
/// Called by the TableView to determine how many sections(groups) there are.
/// </summary>
public override int NumberOfSections (UITableView tableView)
{
return this._tableItems.Count;
}
/// <summary>
/// Called by the TableView to determine how many cells to create for that particular section.
/// </summary>
public override int RowsInSection (UITableView tableview, int section)
{
return this._tableItems[section].Items.Count;
}
/// <summary>
/// Called by the TableView to retrieve the header text for the particular section(group)
/// </summary>
public override string TitleForHeader (UITableView tableView, int section)
{
return this._tableItems[section].Name;
}
/// <summary>
/// Called by the TableView to retrieve the footer text for the particular section(group)
/// </summary>
public override string TitleForFooter (UITableView tableView, int section)
{
return this._tableItems[section].Footer;
}
/// <summary>
/// Called by the TableView to retreive the height of the row for the particular section and row
/// </summary>
public override float GetHeightForRow (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
return 109f;
}
/// <summary>
/// Called by the TableView to get the actual UITableViewCell to render for the particular section and row
/// </summary>
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
//---- declare vars
UITableViewCell cell = tableView.DequeueReusableCell (this._customCellIdentifier);
CustomTableViewCell customCellController = null;
//---- if there are no cells to reuse, create a new one
if (cell == null)
{
customCellController = new CustomTableViewCell ();
// retreive the cell from our custom cell controller
cell = customCellController.Cell;
// give the cell a unique ID, so we can match it up to the controller
cell.Tag = Environment.TickCount;
// store our controller with the unique ID we gave our cell
this._cellControllers.Add (cell.Tag, customCellController);
}
else
{
// retreive our controller via it's unique ID
customCellController = this._cellControllers[cell.Tag];
}
//---- create a shortcut to our item
BasicTableViewItem item = this._tableItems[indexPath.Section].Items[indexPath.Row];
//---- set our cell properties
customCellController.Heading = item.Name;
customCellController.SubHeading = item.SubHeading;
if (!string.IsNullOrEmpty (item.ImageName))
{
customCellController.Image = UIImage.FromFile ("Images/" + item.ImageName);
}
//---- return the custom cell
return cell;
}
}
//========================================================================}
}
If you examine this, it looks almost identical to our BasicTableViewSource class but with a few changes.
The first thing to note is our declarations:
//---- declare vars protected List<BasicTableViewItemGroup> _tableItems; protected string _customCellIdentifier = "MyCustomTableCellView"; protected Dictionary<int, CustomTableViewCell> _cellControllers = new Dictionary<int, CustomTableViewCell>();
We’ve added a new item called _cellControllers. We’ll see how we use this later, but it will hold a collection of our customized cell controllers.
The next addition is the GetHeightForRow method:
/// <summary>
/// Called by the TableView to retreive the height of the row for the particular section and row
/// </summary>
public override float GetHeightForRow (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
return 109f;
}
When we created our custom cell controller, we made it taller than the usual cell. During databinding, CocoaTouch needs to know how much room to allocate for our cell. It does this by calling the GetHeightForRow method. If we didn’t tell it our new size, it would try to fit it in the standard size cell area, and we’d see weird results.
If we open up our cell in Interface Builder and look at the Size Inspector, it tells us the height of our cell. In my case, it’s 109 pixels, but yours may be slightly different.
The final piece that has changed is our GetCell method:
/// <summary>
/// Called by the TableView to get the actual UITableViewCell to render for the particular section and row
/// </summary>
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
//---- declare vars
UITableViewCell cell = tableView.DequeueReusableCell (this._customCellIdentifier);
CustomTableViewCell customCellController = null;
//---- if there are no cells to reuse, create a new one
if (cell == null)
{
customCellController = new CustomTableViewCell ();
// retreive the cell from our custom cell controller
cell = customCellController.Cell;
// give the cell a unique ID, so we can match it up to the controller
cell.Tag = Environment.TickCount;
// store our controller with the unique ID we gave our cell
this._cellControllers.Add (cell.Tag, customCellController);
}
else
{
// retreive our controller via it's unique ID
customCellController = this._cellControllers[cell.Tag];
}
//---- create a shortcut to our item
BasicTableViewItem item = this._tableItems[indexPath.Section].Items[indexPath.Row];
//---- set our cell properties
customCellController.Heading = item.Name;
customCellController.SubHeading = item.SubHeading;
if (!string.IsNullOrEmpty (item.ImageName))
{
customCellController.Image = UIImage.FromFile ("Images/" + item.ImageName);
}
//---- return the custom cell
return cell;
}
This method starts out very similar to our BasicTableViewSource class, we try and pull a cell to reuse from the pool via the DequeueReusableCell call. After this, things get a little more complicated. If we can’t get a cell to reuse, we have to create a new one. This starts by creating the our CustomTableViewCell class which is the controller that actually contains our custom cell.
Once we’ve created our controller, we assign the cell variable to be the Cell property of our controller.
We then add a unique identifier to our cell, so that we can associate it with the correct CustomTableViewCell controller later. We’re using Environment.TickCount because it provides a reasonable level of uniqueness. If we wanted to, we could use the Guid.NewGuid, but that’s a fairly expensive call.
Next, we use that same tag identifier that we created to store the CustomTableViewCell controller in a Dictionary object.
The reason why we do this become fairly obvious in the next lines:
else
{
// retreive our controller via it's unique ID
customCellController = this._cellControllers[cell.Tag];
}
If we did find a cell to reuse, we need to set its properties via the controller. In order to do this, we have to pull it out of our Dictionary using the identifier that we used before.
That covers the bulk of changes to this class. The next few lines underwent minor changes in order to change the properties on the controller class, rather than the cell itself.
Ok! We’re almost done. Open up Main.cs, and we’re going to make two changes in our AppDelegate class to use our CustomTableViewSource instead of the BasicTableViewSource.
First, comment out this line:
BasicTableViewSource _tableViewSource;
And add this line, right after it:
CustomTableViewSource _tableViewSource;
Next, in our CreateTableItems method, comment out this line:
this._tableViewSource = new BasicTableViewSource(tableItems);
And add this line, right after it:
this._tableViewSource = new CustomTableViewSource(tableItems);
Our updated AppDelegate class should now look like:
// The name AppDelegate is referenced in the MainWindow.xib file.
public partial class AppDelegate : UIApplicationDelegate
{
//BasicTableViewSource _tableViewSource;
CustomTableViewSource _tableViewSource;
// This method is invoked when the application has loaded its UI and its ready to run
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
this.CreateTableItems ();
this.tblMain.Source = this._tableViewSource;
// If you have defined a view, add it here:
// window.AddSubview (navigationController.View);
window.MakeKeyAndVisible ();
return true;
}
// This method is required in iPhoneOS 3.0
public override void OnActivated (UIApplication application)
{
}
/// <summary>
/// Creates a set of table items.
/// </summary>
protected void CreateTableItems ()
{
List<BasicTableViewItemGroup> tableItems = new List<BasicTableViewItemGroup> ();
//---- declare vars
BasicTableViewItemGroup tGroup;
//---- birds
tGroup = new BasicTableViewItemGroup() { Name = "Birds", Footer = "Birds have wings, and sometimes use them." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Crow", SubHeading = "AKA, Raven.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Chicken", SubHeading = "Males are called roosters.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Turkey", SubHeading = "Eaten at thanksgiving.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- fish
tGroup = new BasicTableViewItemGroup() { Name = "Fish", Footer = "Fish live in water. Mostly." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Trout", SubHeading = "Rainbow is a popular kind.", ImageName = "TargetIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Salmon", SubHeading = "Good sushi.", ImageName = "PawIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Cod", SubHeading = "Flat fish.", ImageName = "LightBulbIcon.png" });
tableItems.Add (tGroup);
//---- mammals
tGroup = new BasicTableViewItemGroup() { Name = "Mammals", Footer = "Mammals nurse their young." };
tGroup.Items.Add (new BasicTableViewItem() { Name = "Deer", SubHeading = "Bambi.", ImageName = "PushPinIcon.png" });
tGroup.Items.Add (new BasicTableViewItem() { Name = "Bats", SubHeading = "Fly at night.", ImageName = "TargetIcon.png" });
tableItems.Add (tGroup);
//this._tableViewSource = new BasicTableViewSource(tableItems);
this._tableViewSource = new CustomTableViewSource(tableItems);
}
}
Run the application, and you should now get this:

If you change your table style to Grouped in Interface Builder, save your changes, and then run it again, you should get something like:

Congratulations! That was a lot of work, but you know how to customize tables with MonoTouch. Now that we’ve got this all working, let’s look at a few more considerations when working with custom cells.
If not done properly, using the UITableView can have tremendous performance issues. This is especially true if you have many rows. The iPhone, while impressive, has a limited processor, and in order to get the responsiveness that users expect with iPhone applications, there are several performance optimizations that you should consider.
Additionally, it is advisable to deploy to the device early and often and evaluate the performance of your table. The simulator is just that, a simulator, and not the actual device. It operates much faster than the device does, and if you rely on it to gauge the performance of your application, you will almost certainly be disappointed when you deploy to the device.
Furthermore, when you do test your tables, if the users can add their own rows, you should test your application with many more rows than you expect a user to add. If it is still snappy and responsive, you know that you’ve built it correctly, and your users won’t be disappointed with its performance.
For more examples showing performance optimizations, check out Apple’s TableViewSuite example application.
It’s written in objective-c, but the concepts are the same.
I got the first part of the tutorial working fine...but when I tried to create the custom cell (5 times) I can't seem to get it working. I get an object reference not set to an instance of an object exception whenever it hits this code block:
public UITableViewCell Cell
{
get { return this.cellMain; }
}
public string Heading
{
get { return this.lblHeading.Text; }
set { this.lblHeading.Text = value; }
}
public string SubHeading
{
get { return this.lblSubHeading.Text; }
set { this.lblSubHeading.Text = value; }
}
public UIImage Image
{
get { return this.imgMain.Image; }
set { this.imgMain.Image = value; }
}
It is like the class is not connecting up with the designer code, but I followed the instructions several times all with the same results. Any assistance would be greatly appreciated.
Thanks,
Ron
Hey ron, quick question for you 1) did you get the constructor change in? and 2) does the sample code work for you?
I got it working...I forgot the constructor change.
Question I'm trying to no avail to build a custom menu in ib and then try to show it over the current view...it should just occupy a fraction of the parent view. I can't seem to make this work...it shows in the center but the views controls won't show up. I need to figure out how to display the subview at a specified size and location...and display it's controls...do you know how I can achieve this? Do you know of any tutorials that illustrate this? Please note that I don't want to use the navigation controller and swap views...I want the view to be a floating menu over the current view.
Thank you!
Ron
Hi,
I found this a very useful article, thanks for posting it. I tried to implement this as a subview of a view, so I had 2 subviews within one view, the view with the table appeared on the bottom view on a main view within the app (this is an iPad app). The table is constructed ok, but when I try to scroll down the table I get a:
CustomTableViewSource respondsToSelector:]: message sent to deallocated instance 0xcd06560
When I implement the table in a separate view the table works perfectly..
Do you know if it is possible to implement the table in this way, or even better do you know why it's trying to send a message to a deallocated instance?
Many Thanks,
Stuart
First of all great article, really helped me for what I needed.
I had a little issue, where it seems that when the field name of the cell was 'view', then
the property would yield null, as if it wasn't loaded yet, but all other fields were loaded.
So when I renamed the field name from 'view' to something else(All in Interface Builder of course) then it worked
like a charm.
Just thought it was worth mentioning if anyone else encountered it.
Thanks!
When I added the Custom View it is not doing anything after doing a successfull build!! I have checked the code thoroughly against supplied code and debugged it and can't understand what is happening. Using Monodevelop 2.4.2 version. Would appreciate any input.
thanks
Hello,
Firstly thanks for this article.
I did everything and it works in simulator. But when i try to run in iPhone device. I m getting error in here :
MonoTouch.Foundation.NSBundle.MainBundle.LoadNib ("LanguageCell", this, null);
and error is :
Objective-C exception thrown. Name: NSUnknownKeyException Reason: [<LanguageCell 0x6e0ccd0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key img_flag.
Thanks for help...</languagecell>
John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.
Kevlin Henney examines code samples to see what can be learned from them starting from the premise that one won’t write great code unless he knows how to read it.
Jason Ayers share the observations he made watching a team of developers collaborating in real time on the same code base, pushing XP, pair programming and continuous integration to their extremes.
Michael Snoyman presents Yesod, a web framework written in Haskell and containing a web server, templating, ORM, libraries (templating, gravatar, etc.).
Richard Kreuter and Kyle Banker on how to avoid classical RDBMS transactional systems by using compensation mechanisms, transactional messaging or transactional procedures.
Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.
One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.
InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.
7 comments
Watch Thread Reply