Auto Layout, Beer Drinkin, iOS, Uncategorized

Stretchy UITableView Headers with Xamarin

The Yahoo News Digest app includes a couple of interesting user interface elements that I wanted to use within my own apps. The feature that I was most keen to recreate was the stretching UITableViewHeader. Its an effect seen in lots of iOS (sometimes referred to as a parallax header). As Beer Drinkin is going to support multi-tasking on iOS, I needed to ensure my implementation continues to support Storyboards and Auto Layout. Fortunately it proved very simple to get everything setup. In this blog post I’ll be sharing how I went about implementing it

beerdrinkinStretchy

Setting up the Storyboard

Adding a header view

To get started I opened my existing Storyboard and selected the UIViewController that requires the tableview header. In my case the scene (or view controller) isn’t a UITableViewController because I require a floating ‘Check in’ button to be visible at all times. Its worth noting that all the steps in the tutorial work with both UITableViewControllers and UIViewControllers.

Screen Shot 2016-02-01 at 11.36.06

Once I had the storyboard loaded, I dragged a UIView from the toolbox and made sure to insert it above the UITableViewCells as a the header view for the table. I then added a UIImageView to the new UIView and set its constraints to be 0,0,0,0. This way when the parent view (the UIView) resizes, the UIImageView will resize as well. I also made sure to set the UIImageView view mode property to Aspect Fit, which makes sure the image looks great no matter what size the view.

Screen Shot 2016-02-01 at 11.39.13

Adding some C#

Adding the resize code

If I were to have run this now, the table header would be displayed but wouldn’t resize with scroll events. To add scaling, I needed to add a code into my ViewController to setup the stretchy goodness that I wanted.

Because I use the header views height in a number of locations throughout the beer description view controller, I went ahead and created a variable rather than scattering magic numbers over my class.

private nfloat headerViewHeight = 200;

Managing the header view

To allow me to manage the table header, I needed to remove it from the UITableView and keep it as a variable for use later. To do this I created a variable in the beer description view controller.

private UIView headerView;

When we load the view controller, we’ll want to set our headerView variable and then set the UITableViews header property to null. This means the tableview has no header view to manage anymore, instead I’ve taken control of the view which allows me to ensure it resizes correctly as the table view scrolls.Despite having just removed the header view from the UITableView, I actually want to go ahead and add it to the table view hierarchy (but not as the header view property of the UITableView)

 headerView = tableView.TableHeaderView;
 tableView.TableHeaderView = null;
 tableView.AddSubview (headerView);
 tableView.ContentInset = new UIEdgeInsets (headerViewHeight, 0, 0, 0);
 tableView.BackgroundColor = UIColor.Clear;

Listening to TableViewDidScroll

In order to successfully respond to the DidScroll event of the UITableViewSource, I’ll need to create an event in the table views delegate. This is because of an issue with the UITableView DidScroll event not firing when a delegate has been set.

public override void Scrolled (UIScrollView scrollView)
{
    DidScroll ();
}

public event DidScrollEventHandler DidScroll;

We can now hook up the table DidScroll event with a small piece of logic for resizing the view.

//Update Tableview
tableView.Source = new BeerDescriptionDataSource(ref cells);
var deleg = new DescriptionDelegate (ref cells);
deleg.DidScroll += UpdateHeaderView;
tableView.Delegate = deleg;

tableView.ReloadData ();
View.SetNeedsDisplay ();
//...
void UpdateHeaderView ()
{
    var headerRect = new CGRect (0, -headerViewHeight, tableView.Frame.Width, headerViewHeight);
    if (tableView.ContentOffset.Y < -headerViewHeight)
    {
        headerRect.Location = new CGPoint (headerRect.Location.X, tableView.ContentOffset.Y);
        headerRect.Size = new CGSize (headerRect.Size.Width, -tableView.ContentOffset.Y);
    }
    headerView.Frame = headerRect;
}

Conclusion

Its very easy to use this approach to add resizing animations to any number of controls within your UITableView. My favourite part of this solution is that it works perfectly across all iOS devices and doesn’t force me to drop support of Autolayout.

7 thoughts on “Stretchy UITableView Headers with Xamarin”

  1. Hi Mike,

    Can you go through how you added the rest of the elements in the storyboard. Your storyboards look professional and well done and I always stuggle with the setting up custom tableviews with storyboards.

    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