UITableViews Part 3: Showing a View Over a UITableViewCell in Xamarin iOS

An iOS UI is comprised of a view, which is what the user sees, and a controller, which is the functional logic. A view can have one or many subviews, but this is not required. Within iOS, there are specialized controlles that work with a particular type of view. For instance, the UITableViewController works with a UITableView as it’s user interface.

Step 1: Initial Setup

Setup a UITableViewController, and a view and/or controller to overlay. There is nothing complex here, and I’m not going to go into any more specificity.

Step 2: Setup Globals

We’ll need a reference to our controller to overlay (within our custom UITableViewController class), so that we only have one static reference to use as the popup. I created the controller in the ViewDidLoad method.

private LinkItemActionController _actionController = null;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();

_actionController = new LinkItemActionController ();
_actionController.View.Hidden = true;
//My own custom method
this.AddViewControllerToView (_actionController, this.View);

Step 3: Responding to the Selection

Now that we have a controller in place, anytime an item is selected, we need to change the current frame that the controller’s view draws in. On click of a cell, the UITableViewSource triggers the RowSelected delegate method. From here, we can notify the controller to draw the LinkItemActionController view within the bounds of the cell. Below is a snippet of how I do that.

//Data source for tableviewcontroller
protected class LinksSource : Nucleo.UI.BaseTableViewSource
{
public event EventHandler ItemSelected;

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
	var cell = this.GetCell (tableView, indexPath);

	var dataItem = this.GetDataItem (indexPath);
	if (dataItem == null)
		return;

	if (ItemSelected != null)
		ItemSelected (this, new SelectEventArgs { Cell = cell, DataItem = dataItem, IndexPath = indexPath });

	tableView.DeselectRow (indexPath, true);
}

}

//controller, responds to ItemSelected event
public partial class LinksController : Nucleo.Controllers.BaseUITableViewController 
{

void HandleItemSelected (object sender, SelectEventArgs e)
{
	var rect = this.TableView.RectForRowAtIndexPath (e.IndexPath);
	_actionController.View.Hidden = false;
	_actionController.View.Frame = rect;
	_actionController.RefreshUI(rect.Width - 40, rect.Height);

	_selectedItem = e.DataItem;
}

In this snippet, the UITableViewSource (from the snippet given in a previous post) notifies the controller that the row was selected. An ItemSelected event bubbles up to the controller, and the HandleItemSelected method handles loading the UI. First, it uses RectForRowAtIndexPath to get the underlying frame to use for the drawing. Next, the view is shown and given the cell rectangle. Lastly, a RefreshUI method does the actual drawing of the buttons, and we store the current selected item for safekeeping.

Step 4: Loading the Buttons

When I first learned how to frame a house, I learned about how to place the studs that support a wall every 16 inches, except for the second stud, which is 15 1/4 inches away from the first stud. The reason for this is simple: to make sure the plywall overlaps a stud evenly on both sides (essentially centering each end evenly over the stud). We’re doing something similar here; we’re going to try to render each button evenly over the cell row, but we have to offset the first button by half of the width to ensure each image appears correctly within its quarter segment (as there are 4 buttons, I broke each out evenly). To do this, I used the following code and a little bit of math to do it (this is our LinkItemActionController):

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

	this.View.BackgroundColor = UIColor.FromRGBA (255, 154, 35, 50);

	//TODO: display evenly-spaced buttons
	_cancelButton = this.CreateButton ("icon_back.png");
	_cancelButton.TouchUpInside += HandleCancel;
	this.View.AddSubview (_cancelButton);

	_shareButton = this.CreateButton ("icon_share.png");
	_shareButton.TouchUpInside += HandleShare;
	this.View.AddSubview (_shareButton);

	_deleteButton = this.CreateButton ("icon_delete.png");
	_deleteButton.TouchUpInside += HandleDelete;
	this.View.AddSubview (_deleteButton);

	_viewButton = this.CreateButton ("icon_view.png");
	_viewButton.TouchUpInside += HandleView;
	this.View.AddSubview (_viewButton);
}

public void RefreshUI(float availableWidth, float availableHeight)
{
	var quarterWidth = availableWidth / 4f;
	var centerWidth = quarterWidth / 2f;
	var currentY = (availableHeight - IMAGE_SIZE) / 2f;
	var currentX = centerWidth - (IMAGE_SIZE / 2f);


	_cancelButton.Frame = new RectangleF (currentX, currentY, IMAGE_SIZE, IMAGE_SIZE);
	currentX += quarterWidth;

	_shareButton.Frame = new RectangleF (currentX, currentY, IMAGE_SIZE, IMAGE_SIZE);
	currentX += quarterWidth;

	_deleteButton.Frame = new RectangleF (currentX, currentY, IMAGE_SIZE, IMAGE_SIZE);
	currentX += quarterWidth;

	_viewButton.Frame = new RectangleF (currentX, currentY, IMAGE_SIZE, IMAGE_SIZE);

}

The IMAGE_SIZE constant is 44 pixels. As a rough example, assume that the total width is 400 pixels. Each quadrant is 100 pixels wide. We want the button to be in the center of the quadrant, so we subtract half of the 100 pixels to arrive at 50. We also need to ensure that we factor in the button image width, which is 44 pixels wide; not factoring this in would throw off the centering, and therefore the algorithm also subtracts another 22 pixels, arriving at a first coordinate value of 28,0 (the cell happens to have a height of 44, so no offset there). Every image afterward has a value of 100 added to it, leaving the next coordinates as 128,0; 228,0; 328,0, with the end of each image finishing at coordinates 72,0; 172,0; 272,0; 372;0.

Finish

Below is a snapshot of the finished product. While not the final color scheme and icon colors, it gives you an idea of what our final product has the potential to become.

iosviewoverscreenshot

Advertisements

One thought on “UITableViews Part 3: Showing a View Over a UITableViewCell in Xamarin iOS

  1. First off I would like to say wonderful blog! I had a quick question which I’d
    like to ask if you don’t mind. I was interested to know
    how you center yourself and clear your head prior to writing.
    I have had a hard time clearing my mind in getting my ideas out.
    I truly do enjoy writing but it just seems like the
    first 10 to 15 minutes tend to be wasted just trying to
    figure out how to begin. Any ideas or tips? Appreciate it!

    Like

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