UITableView with custom UITableViewCell using MonoTouch and Xcode 4
Before reading this you will need a basic understanding of MonoTouch, go to www.xamarin.com and read their tutorials if you want to learn more, they are great.
We just started taking a look at creating iOS apps using the incredible MonoTouch framework (now developed by a company called Xamarin).
MonoTouch allows the user to create apps using C# instead of Objective-C. This however is the main change, you will still need to use the original CocoaTouch classes (but in C#), a Mac and the Xcode 4 tool for designing your user interface. The actual coding takes place in MonoDevelop a great tool similar to Visual Studio but working on the Mac.
Pretty soon when working with iOS apps you will start dealing with the UITableView for creating lists and navigation views. This also means that you pretty soon will find yourself trying to find out how to create custom cells in these tables (the built in ones only support basic things like one line of text and an icon).
When confronted with this I started reading different blog posts and forum posts on how to do this. However I saw a lot of issues, confusion and strange implementations. Also none of the blog posts showed how to do it in Xcode 4. After reading some good books (that did not use MonoTouch) and doing some testing myself I finally got it working, this is how I did it.
Step 1. Create a new MonoTouch project using MonoDevelop, choose an Empty iPhone project and give it a good name.
Step 2. Add an iPhone View Controller and name it “ListViewController”. It’s in this code behind and .xib file that the actual list will be displayed and controlled.
Step 3. Change the base class of the ListViewController from “UIViewController” to “UITableViewController”.
public partial class ListViewController : UITableViewController
{
// ...
Step 4. In the AppDelegate.cs file add the following line of code after the “window = “ part.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new ListViewController();
// ...
Step 5. Now if you try running this application it will fail. That is because the ListViewController expects a UITableView but is now getting an UIView because of how things are set up in the .xib file. So let’s open that file in Xcode 4!
Step 6. From the object library drag a Table View and add it above the View object for your interface (see picture below). Now remove the original View object so that we only have one Table View.
Step 7. Before trying to run the app we will need to connect the view outlet of the “File’s Owner”, which in this case will be your ListViewController class. Because we removed the original View object and replaced it with a Table View the connection got lost. Simply click on “File’s Owner” and drag the view outlet line onto the Table View (see picture below). If you try running the app now it should show up in your simulator with an empty lists. If it's not working make sure you have saved the .xib changes.
Step 8. As we mentioned earlier the list is now empty. To fill the list with data we need to create an object that inherits the abstract class “UITableViewSource”. This class has a number of methods that you override to tell your table view how many rows and sections it should have and what cells it should display. The most important one, the one for cells, is called “GetCell” and it is here we will write some code to allow us to use custom cells. But first let’s create an Empty class called “ListSource” and inherit the UITableViewSource. Don’t forget to add a reference to the “MonoTouch.UIKit” namespace.
using System;
using MonoTouch.UIKit;
namespace CustomUITableViewCellSample
{
public class ListSource : UITableViewSource
{
public ListSource() {
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
// TODO: Implement - see: http://go-mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute
}
public override int RowsInSection (UITableView tableview, int section)
{
// TODO: Implement - see: http://go-mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute
}
}
}
also create a new instance of this class inside your ListViewController and assign it to the TableView.Source property:
public partial class ListViewController : UITableViewController
{
public ListViewController () : base ("ListViewController", null)
{
this.TableView.Source = new ListSource();
}
// ...
Step 9. Let’s add some sample data to this list before creating custom cells. Here we might ask ourselves where that data should exist. Inside the ListViewController class? Inside the ListSource class? When doing this in Objective-C you use something called protocols which resembles interfaces but without the need of implementing all methods. Since protocols does not exist in C# and interfaces would force you to implement a lot of methods that you normally aren’t interested in abstract classes are used instead. The problem with this is that you simply can’t have all code inside your view controller class for example but must have it separated in different classes. To solve this many declare these additional classes inside their view controller classes to at least keep all the code in the same place. You would then hold the data in the view controller class and pass it as a reference to your source class. In this sample I have not done that (I normally use another method which I might show in a future blog post) but instead provide a really simple “hard-coded” solution inside the source class.
using System;
using MonoTouch.UIKit;
using System.Collections.Generic;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
namespace CustomUITableViewCellSample
{
public class ListSource : UITableViewSource
{
private List<string> _testData = new List<string> ();
public ListSource ()
{
_testData.Add ("Green");
_testData.Add ("Red");
_testData.Add ("Blue");
_testData.Add ("Yellow");
_testData.Add ("Purple");
_testData.Add ("Orange");
}
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
// Reuse a cell if one exists
UITableViewCell cell = tableView.DequeueReusableCell ("ColorCell");
if (cell == null) {
// We have to allocate a cell
cell = new UITableViewCell(UITableViewCellStyle.Default, "ColorCell");
// Set initial data
cell.TextLabel.Text = _testData[indexPath.Row];
} else {
// This cell has been used before, so we need to update it's data
cell.TextLabel.Text = _testData[indexPath.Row];
}
return cell;
}
public override int RowsInSection (UITableView tableview, int section)
{
return _testData.Count;
}
}
}
We can see that the GetCell part tries to reuse an already created cell if it can, using the DequeueReusableCell method. This is an important concept when using the UITableView and is one of the reasons of it’s great performance. You can find more information about this on various blogs or by reading Apples documentation about it found here (it's in Objective-C but you will probably be able to understand the important parts).
Running the app now will look like this:
Step 10. Customize! Now let’s create a custom cell that can show a button (could be any control though) with the same color as the text. To do this we will create a code-behind file for the new cell and a .xib file for it. Because no such file template exists in MonoTouch yet (they should really add one!) I use a little trick to set up the basic stuff.
Start by adding a new iPhone View Controller to the project and name it “CustomListCell”. We will not actually be using a ViewController for this but it’s an easy way to get MonoTouch to generate the .xib file and partial classes that we need for everything to work.
Step 11. Change the base class of the CustomListCell to “UITableViewCell”. Also remove all the overriden methods and also the constructors. Change the constructors to match the following:
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace CustomUITableViewCellSample
{
public partial class CustomListCell : UITableViewCell
{
public CustomListCell () : base()
{
}
public CustomListCell (IntPtr handle) : base(handle)
{
}
}
}
Try compiling making sure everything is still working.
Step 12. Now it’s time to customize the cell using Xcode. Open the CustomListCell.xib file in Xcode 4.
Step 13. Remove the View object and replace it with a Table View Cell (you can find it in the Object Library, see lower right corner in the picture below). This was originally a View Controller and that is why there was a View in there.
It should now look like this:
Step 14. Click the Table View Cell and in the Attributes Inspector set the “Reuse Identifier” to “ColorCell”. If you forget to do this the cell recycling will not work and the performance of you app will not be optimal.
Step 15. This step is very important! Missing this (which I initially did) means your app will crash.
Click on the Table View Cell. Now in the Identity Inspector change the class to “CustomListCell”.
Finally click on the “File’s Owner”, we need to make some changes here.
In many blog posts about this I have seen people setting this to their custom cell class also. I believe that is wrong. Instead you should set the owner to be the actual table view that holds all the cells. This means you do not have to create an extra custom cell instance to set for the owner. Why should a custom cell be owned by a different custom cell :) ? So change this to UITableView.
Step 16. Time to customize the cell. Let’s just add a big button in the middle of the cell (also from the Object Library).
Step 17. To be able to customize things like the text of the button the color of it etc. we need to be able to access it from code. So we need to create an outlet for this button so that we can access it from our CustomListCell.cs file.
Switch to the assistant editor mode.
Make sure you have the view in one panel and the CustomListCell.h file in the other. Ctrl-drag from the button into the .h file to create an outlet. Name it “testButton”.
After pressing "Connect" you get this.
For a more advanced cell you would simply add more views and outlets and possibly some actions.
Save everything and return to MonoDevelop.
Step 18. Time to get back to coding! In the CustomListCell.cs file let’s add a method called “UpdateWithData” taking a single string as an argument. This method will refresh the cell with new data. This is needed since the cell can get reused to possibly display different data. Based on the data we update the text of the button and the color of the text. Once again a more advanced cell would probably take multiple arguments of more complex data.
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace CustomUITableViewCellSample
{
public partial class CustomListCell : UITableViewCell
{
public CustomListCell () : base()
{
}
public CustomListCell (IntPtr handle) : base(handle)
{
}
public void UpdateWithData (string text)
{
UIColor textColor = UIColor.Black;
switch (text.ToLower()) {
case "green":
textColor = UIColor.Green;
break;
case "red":
textColor = UIColor.Red;
break;
case "blue":
textColor = UIColor.Blue;
break;
case "yellow":
textColor = UIColor.Yellow;
break;
case "purple":
textColor = UIColor.Purple;
break;
case "orange":
textColor = UIColor.Orange;
break;
}
testButton.SetTitleColor (textColor, UIControlState.Normal);
}
}
}
Now this could basically be done using a regular UITableViewCell and setting the color of it's text label but for simplicity we only used a regular button in our custom cell this time (a regular UITableViewCell can't have buttons in them!).
Step 19. Almost there! To actually get our Table View to use these custom cells we need to rewrite our “GetCell” method. To be able to use a cell from a .xib file we need to use a method called LoadNib located in the NSBundle.MainBudle (more info from Apple can be found here). Change your code in the “GetCell” method of the LoadSource class to the following:
using System;
using MonoTouch.UIKit;
using System.Collections.Generic;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
namespace CustomUITableViewCellSample
{
public class ListSource : UITableViewSource
{
private List<string> _testData = new List<string> ();
public ListSource ()
{
_testData.Add ("Green");
_testData.Add ("Red");
_testData.Add ("Blue");
_testData.Add ("Yellow");
_testData.Add ("Purple");
_testData.Add ("Orange");
}
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
// Reuse a cell if one exists
CustomListCell cell = tableView.DequeueReusableCell ("ColorCell") as CustomListCell;
if (cell == null) {
// We have to allocate a cell
var views = NSBundle.MainBundle.LoadNib ("CustomListCell", tableView, null);
cell = Runtime.GetNSObject (views.ValueAt (0)) as CustomListCell;
}
// This cell has been used before, so we need to update it's data
cell.UpdateWithData (_testData [indexPath.Row]);
return cell;
}
public override int RowsInSection (UITableView tableview, int section)
{
return _testData.Count;
}
}
}
Step 20. Run your app! If you have been following this post it should look something like this!
Congratulations on creating your own custom UITableViewCell using MonoTouch and Xcode 4!
Grab the sample project on GitHub:
https://github.com/arcticmill/CustomUITableViewCellSample
Thank you for reading!




















