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.

Auto Layout, iOS

Auto Layout 101 with Xamarin

Until recently I’d done an amazing job of avoiding Auto Layouts on anything other than demo apps, instead opting to create my layouts with springs and structs. All my apps within the App Store use the old approach, which although being exceptionally easy to create, its limited when running across all the different form factors that iOS runs on.

With multitasking on the iPad requiring Auto Layouts, I thought its probably about time that I took the time to learn to love the contraversal layout engine.

What is Auto Layout?

Auto Layout is a constraints based layout system for iOS, tvOS and OS X. It allows me to create adaptive user interaces that respond appropriatly to changes in screen size and orientation.

Auto Layouts is supported in both Xamarin Studio and Visual Studio when using Xamarin.iOS but will not be applicable to Xamarin.Forms developers. Xamarin.Forms developers dont need to worry as Forms apps already work fantasticlly on iPad with multitasking!

Layout contraints

Contraints are a mathematical representation of the relationship between views. The NSLayoutContraint class is used to create contraints on both iOS and OS X but for the most part, you’ll want to create contraints using our iOS Designer rather than programatically as its much easier.

Auto Layout has a number of constraints which include size, alignment and spacing. By providing each view within a scene with constraints, Auto Layout will determine the frame of each view at runtime.

One really important tip which will help you solve Auto Layout issues is to remember: Most controls will require constraints that define its height, width, x position and y position. 

Getting Started

To get started, I want to center a UIView within my scene. I want to remain in the center of the scene no matter what size of device its running on.

Screen Shot 2015-11-25 at 15.13.13

Above you can see that I’ve added a UIView to my scene and set its background colour to blue. Although it looks like its center to my scene, no iOS device actually has this form factor. We can use the View As drop down to simulate the different size. Lets see how this would look on a 5s with no constraints.

Screen Shot 2015-11-25 at 15.13.41

We can see that my UIView isn’t position correctly! To resolve this, lets go ahead and add some contraints.

I’m going to lock its height and width. To do this, I’ll click the handle bar button.

handlebars

This has now locked the width and height of my view so that no matter what device I’m running on, the box will always remain the same dimentions. You’ll note that the lines are currently orange. This means that the control contains some errors with its contraints.

If you recall back to my pro tip, you’ll know that we’re only 50% of the way to completing the contraints for this view. We have yet to provide Auto Layouts with any information on where to position this view.

To do this, I clicked on the orange button in the middle of the view and connect it to the verticle line running through the scene. I repeat this for the horizontal line. This is telling Auto Layout that I want this views X and Y to be centered on the center of the super view.

center

When the view has 4 contraints (width, height, x and y), you’ll see the lines now turn to blue. This marks a valid set of contraints for the view and lets us know everything is valid. If you see orange lines, its Auto Layouts telling you that somethings gone wrong. Dont worry if you see orange whilst your still editing, its perfectly normal.

Screen Shot 2015-11-25 at 15.18.44

Wrapping up

This is a crazy simple demo of Auto Layouts but provides you with the basic you need to get started.

Come back in a few weeks to see more Auto Layout goodness as I convert an existing login screen to use Auto Layout.