Xamarin.iOSCalculating variable row height in GetHeightForRow

Remarks

Calculating row heights might be expensive and scrolling performance can suffer if you have larger amounts of data. In that scenario, override UITableViewSource.EstimatedHeight(UITableView, NSIndexPath) to quickly provide a number sufficient for rapid scrolling, e.g.,:

public override nfloat EstimatedHeight(UITableView tableView, NSIndexPath indexPath) 
{ 
    return 44.0f; 
}

Using GetHeightForRow

To set a custom row height, override UITableViewSource.GetHeightForRow(UITableView,NSIndexPath):

public class ColorTableDataSource : UITableViewSource
{
    List<DomainClass> Model { get; set; }

    public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
    {
        var height = Model[indexPath.Row % Model.Count].Height;
        return height;
    }
    
    //...etc ...
}

The domain class for the table (in this case, it has 1 of 3 random colors and 1 of 3 random heights):

public class DomainClass
{
    static Random rand = new Random(0);
    public UIColor Color { get; protected set; }
    public float Height { get; protected set; }

    static UIColor[] Colors = new UIColor[]
    {
        UIColor.Red,
        UIColor.Green,
        UIColor.Blue,
        UIColor.Yellow
    };

    public DomainClass()
    {
        Color = Colors[rand.Next(Colors.Length)];
        switch (rand.Next(3))
        {
            case 0:
                Height = 24.0f;
                break;
            case 1:
                Height = 44.0f;
                break;
            case 2:
                Height = 64.0f;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    public override string ToString()
    {
        return string.Format("[DomainClass: Color={0}, Height={1}]", Color, Height);
    }
}

Which looks like:

Variable table row heights

Here is a complete program to demonstrate the technique:

using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;
using System.Drawing;

namespace SingleFileTableViewSolution
{
  //Model
  public class DomainClass
  {
    static Random rand = new Random(0);
    public UIColor Color { get; protected set; }
    public float Height { get; protected set; }

    static UIColor[] Colors = new UIColor[]
    {
        UIColor.Red,
        UIColor.Green,
        UIColor.Blue,
        UIColor.Yellow
    };

    public DomainClass()
    {
        Color = Colors[rand.Next(Colors.Length)];
        switch (rand.Next(3))
        {
            case 0:
                Height = 24.0f;
                break;
            case 1:
                Height = 44.0f;
                break;
            case 2:
                Height = 64.0f;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    public override string ToString()
    {
        return string.Format("[DomainClass: Color={0}, Height={1}]", Color, Height);
    }
  }

  //Table Source
  public class ColorTableDataSource : UITableViewSource
  {
    List<DomainClass> Model { get; set; }

    public ColorTableDataSource(List<DomainClass> model)
    {
        this.Model = model;
    }

    public override nint RowsInSection(UITableView tableView, nint section)
    {
        return 10000;
    }

    public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
    {
        var cell = tableView.DequeueReusableCell(ColoredTableCell.ID, indexPath);
        cell.ContentView.BackgroundColor = Model[indexPath.Row % Model.Count].Color;

        return cell;
    }

    public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
    {
        var height = Model[indexPath.Row % Model.Count].Height;
        return height;
    }

    public override void CellDisplayingEnded(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
    {
        Console.WriteLine("CellDisplayingEnded on {0}", indexPath);
    }
  }

  public class ColoredTableCell : UITableViewCell
  {
    public static readonly NSString ID = new NSString("ColoredTableCell");
    public static int ClassCount = 0;
    public int myId = 0;

    public ColoredTableCell()
    {
    }

    public ColoredTableCell(IntPtr handle) : base(handle)
    {
        Console.WriteLine("New ColoredTableCell allocated {0} {1}", handle.ToInt64(), ClassCount++);
        myId = handle.ToInt32();
    }

    ~ColoredTableCell()
    {
        Console.WriteLine("ColoredTableCell {0} being finalized.", myId);
    }
  }

  public class ColorTableController : UITableViewController
  {
    String title;

    public ColorTableController(String title, UITableViewSource source) : base()
    {
        this.title = title;
        this.TableView.Source = source;

        this.TableView.RegisterClassForCellReuse(typeof(ColoredTableCell), ColoredTableCell.ID);
        this.TableView.TableHeaderView = new UIView(new RectangleF(0, 0, 500, 200));
        this.TableView.TableHeaderView.BackgroundColor = UIColor.Brown;
    }

    public override void DidReceiveMemoryWarning()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning();
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        Title = title;
    }
  }

  [Register("AppDelegate")]
  public class AppDelegate : UIApplicationDelegate
  {
    UIWindow window;
    ColorTableController viewController;

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        var models = new List<DomainClass>();
        for (int i = 0; i < 20; i++)
        {
            models.Add(new DomainClass());
        }

        var tableController = new ColorTableController("My Table", new ColorTableDataSource(models));

        window = new UIWindow(UIScreen.MainScreen.Bounds);
        window.RootViewController = tableController;

        window.MakeKeyAndVisible();

        return true;
    }
  }

  public class Application
  {
    static void Main(string[] args)
    {
        UIApplication.Main(args, null, "AppDelegate");
    }
  }
}