Use case for ContainerView #xamarin #iosdev

Think of a scenario where you have multiple subviews within a view in your application. The screen below shows Cinema Details view in my Cineworld app for iOS

iOS Simulator Screen Shot 23 Apr 2015 15.23.01 iOS Simulator Screen Shot 23 Apr 2015 15.23.06 iOS Simulator Screen Shot 23 Apr 2015 15.23.11 iOS Simulator Screen Shot 23 Apr 2015 15.23.18

 

The screen originally consisted of

  • Segment Control
  • Label (Date display)
  • Calendar Button
  • TableView (List by Date)
  • TableView (Current and Upcoming)
  • RatingView
  • Rate count Label
  • Address and Telephone labels
  • Walking / Driving Direction buttons
  • RateView Button
  • Cinema reviews TableView
  • Ad View

The first iteration of this view used Autosizing and as you can imagine there was some cropping for iPhone 4s. To get around it, I started using Autolayout. Can you imagine managing and creating constraints for this many controls ? I tried.. not once but a few times however it never seemed to fully work.

A few times I moaned how in Windows Phone I have above controls in Panorama and how crappy iOS is. I tried both Xamarin Studio and Xcode to set the constraints.. Nada.. It was explained to me that I am using too many controls and I should simplify my view.. was told have a look at ContainerView. So I did

A container view is a view that can host other views. So i got rid of most controls to have

  • Segment control
  • container view
  • Ad control

Screen Shot 2015-04-23 at 15.42.55   Screen Shot 2015-04-23 at 15.41.28

At this point adding constraints to this view was very very simple. Now simplify by adding new View Controllers for each subviews

Screen Shot 2015-04-23 at 15.49.21

 

Screen Shot 2015-04-23 at 15.51.59

They layouts of each subviews is lot simpler now and auto layout was much easier. Now lets look at how we show / hide desired subviews.

private void ShowContainerView(UIViewController vc)
{
	this.AddChildViewController(vc);

	this.CinemaDetailsContainer.AddSubview(vc.View);

	vc.DidMoveToParentViewController(this);
}

private void HideContainerView(UIViewController vc)
{
	vc.WillMoveToParentViewController (null);

	vc.View.RemoveFromSuperview ();

	vc.RemoveFromParentViewController ();
}

The above methods can be used to add / remove view / view controllers from current view.

FilmsByDateViewController GetFilmsByDateViewController()
{
	if (this.filmsByDateVC == null) 
	{
		var vc = this.Storyboard.InstantiateViewController ("FilmsByDateViewController") as FilmsByDateViewController;

		vc.FilmPerformaceDictionary = this.dateFilms;
		vc.Cinema = this.Cinema;

		vc.View.Frame = new CoreGraphics.CGRect(0, 0, CinemaDetailsContainer.Frame.Width, CinemaDetailsContainer.Frame.Height);

		this.filmsByDateVC = vc;
	}

	return this.filmsByDateVC;
}

FilmListViewController GetCurrentFilmsViewController ()
{
	if (this.currentFilmsVC == null) 
	{
		var vc = this.Storyboard.InstantiateViewController ("FilmListViewController") as FilmListViewController;
		var currentFilms = new AllFilmsTableSource (AllFilmsTableSource.FilmListingType.Current, this.Films);
		vc.FilmSource = currentFilms;
		vc.Cinema = this.Cinema;

		vc.View.Frame = new CoreGraphics.CGRect(0, 0, CinemaDetailsContainer.Frame.Width, CinemaDetailsContainer.Frame.Height);

		this.currentFilmsVC = vc;
	}

	return this.currentFilmsVC;
}

FilmListViewController GetUpcomingFilmsViewController ()
{
	if (this.upcomingFilmsVC == null) 
	{
		var vc = this.Storyboard.InstantiateViewController ("FilmListViewController") as FilmListViewController;
		var upcomingFilms = new AllFilmsTableSource (AllFilmsTableSource.FilmListingType.Upcoming, this.Films);
		vc.FilmSource = upcomingFilms;
		vc.Cinema = this.Cinema;

		vc.View.Frame = new CoreGraphics.CGRect(0, 0, CinemaDetailsContainer.Frame.Width, CinemaDetailsContainer.Frame.Height);

		this.upcomingFilmsVC = vc;
	}

	return this.upcomingFilmsVC;
}

CinemaInfoViewController GetCinemaInfoViewController()
{
	if (this.cinemaInfoVC == null) 
	{
		var vc = this.Storyboard.InstantiateViewController("CinemaInfoViewController") as CinemaInfoViewController;
		vc.Cinema = this.Cinema;
		vc.View.Frame = new CoreGraphics.CGRect(0, 0, CinemaDetailsContainer.Frame.Width, CinemaDetailsContainer.Frame.Height);

		this.cinemaInfoVC = vc;
	}

	return this.cinemaInfoVC;
}

The above methods are helpers to instantiate contained view controllers and the code below is the ViewDidLoad showing how Segment control’s ValueChanged shows the correct contained view

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

	this.CinemaSegments.Enabled = false;

	// page init code...

	var filmsDateVC = this.GetFilmsByDateViewController();

	this.CinemaSegments.ValueChanged += (sender, e) => 
	{
		var currentVC = this.GetCurrentFilmsViewController();
		var upcomingVC = this.GetUpcomingFilmsViewController();
		var cinemaVC = this.GetCinemaInfoViewController();

		this.HideContainerView(filmsDateVC);
		this.HideContainerView(currentVC);
		this.HideContainerView(upcomingVC);
		this.HideContainerView(cinemaVC);

		switch(this.CinemaSegments.SelectedSegment)
		{
		case 0:
			this.ShowContainerView(filmsDateVC);
			break;

		case 1:
			this.ShowContainerView(currentVC);
			break;

		case 2:
			this.ShowContainerView(upcomingVC);
			break;

		case 3:
			this.ShowContainerView(cinemaVC);
			break;
		}
	};

	this.CinemaSegments.Enabled = true;

	this.ShowContainerView (filmsDateVC);

}

I similarly simplified a few other views and job done.

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