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
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
At this point adding constraints to this view was very very simple. Now simplify by adding new View Controllers for each subviews
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.