Blog

Follow our blog and stay up to date with everything that has to do with Arcticmill. We tend to blog a little about everything, but our main focus is always keeping you updated on the most recent news about the game(s) we're working on. Pop in from time to time and check out what's new - When we do something, you'll read about it here first!

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!

/Johan Otterud

  • Liam

    Hi There

    Thank you very much for this great tutorial. I am just starting out with MonoTouch coming from using Mono for Android and this is exactly what I needed. As you mentioned all of the other tutorials are out of date and without this I would be completely lost!

    Thanks
    Liam

    • arcticmill

      Glad you liked it Liam! MonoTouch is such a great toolkit.

      Let us know if you have any questions.
      Cheers

  • mike

    Thanks – Great job. Just wish I found your link sooner!

  • Zohaib

    Thank you very much!!!!!!!!

    • arcticmill

      You’re welcome. Glad we could help.

  • Erik Blanken

    I have a question. All of this tutorial worked, except for one thing. I Tried to center the button on the cell with XCode and it looks good in XCode. But when i run it on a device, the button overlaps half of the next cell. Where did this go wrong and what to do to fix this?

    • arcticmill

      Hi Erik!

      Sorry again for our late responses, we have been super busy lately.

      It must have something to do with the height settings for the UITableViewCell. I think there are many solutions to this but one you could try is to override the method GetHeightForRow in your UITableViewSource class. Like so:

      public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
      {
      // Insert needed height value
      return 100.0f;
      }

  • Reint Jan Hoiting

    Many thanks for your quick tutorial!
    the last 2 days i have been doing a couple of different tutorials, but every time I’m getting the same bug.
    it looks like that DeQueueReusableCell( Identifier ) doesn’t recognize my Identifier.

    My table is not getting the correct layout…

    I have uploaded a screenshot from my .net code / xcode / iPad screenshot to illustrate my problem.
    http://reintjanhoiting.nl/upload/Question.png

    Im not getting any error message. Is the anyway how i can make sure that my identifier is not the problem?

    Hope you seen this problem before.

    • arcticmill

      Hi Reint!

      I’m sorry for the late response but we have been very busy finishing our iPhone and iPad game Castle Raid. Did you ever solve this issue? It’s hard to see what is wrong from your screenshot. Everything looks correct to me… If you haven’t fixed this you can get back to us and we will see if we can help you!

  • Thor

    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;
    }

    ******************************

    A cell is reused if possible, but never is there a new cell initialized with an identifier. How could the dequeuing work, if the queued cell have no id?

    gr, Thor

    • arcticmill

      You must set the id of the cell in Xcode. The tutorial should show you how to do that!

  • http://microgroove.com Brett

    Thank you so much for this.

    Lot’s of blog posts out there. This is the only one that has decent screenshots and code that includes the “usings”.

    Thank you!!

    • arcticmill

      No problem! Glad to help!

  • chris

    Hi,

    I followed your tutorial but my cells always show empty … I can see the number of cell is correct but it’s all blank …
    Would you know why?

    • chris

      Nevermind, the problem came from the size of my cell….

  • Alejandro

    Hi, does anyone know how to implement this inside a Monotouch Dialog Custom Element??
    Thanks a lot

  • Rens

    The best article for custom cells in MonoTouch.

    Question, how can I define custom cells with al grouped tableview layout? I now always get the plain tableview…

    Hope you can help me, if not anyways thank you for the article.

    • arcticmill

      Hi! Happy to hear you like it. :) Our pro in this area, and the writer of the post is on holiday until after x-mas. He’ll get back to you!

  • Vinícius E.

    Great article! Thank you very much. It helped a lot =D

  • P. Sami

    Awesome tutorial! Thank you very much for the time and effort you put into this.

    I have a question though, I’ve been trying to do this in the StoryBoard but I’m not sure how can I do the GetCell method (since there is no nib file). Is it even possible or if there is any workaround for that?

  • Pingback: Monotouch iphone custom UITableView | BlogoSfera

  • Dorababu

    Hi good nice job.

    i want to display gridview like images using uitableview
    is it possible to create two images in each row like two columns gridview

    please provide the relevant code

  • Cristian Merli

    It throws an exception in “var views = …”

    “MonoTouch.Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: Could not load NIB in bundle: ‘NSBundle (loaded)’ with name ‘CustomListCell’

    at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:IntPtr_objc_msgSend_IntPtr_IntPtr_IntPtr (intptr,intptr,intptr,intptr,intptr)

    at MonoTouch.Foundation.NSBundle.LoadNib (System.String nibName, MonoTouch.Foundation.NSObject owner, MonoTouch.Foundation.NSDictionary options) [0x00023] in /Developer/MonoTouch/Source/monotouch/src/Foundation/NSBundle.g.cs:425

    at ……ListSource.GetCell (MonoTouch.UIKit.UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath) [0x00021] in c:Users….”

    Do you happen to know why it would not work?

Castle Raid 2 is out!

Castle Raid 2 is THE game to play with friends and family this holiday seasons. The coolest feature of the game is the unique "head 2 head" same device multiplayer, perfect for killing those lingering hours before Santa shows up.
Who will prove to be the best castle raider?
AppStore
 
GooglePlay