Thursday, January 9, 2020

How to use Multiple View Controller in Single Page in Xamarin iOS

Introduction

In this blog, I will explain how to handle multiple view controllers on a single page in Xamarin iOS. If you're interested in learning this, just read the entire post. Since there is no default functionality for showing multiple view controllers in iOS. Some of the application designs are complicated too. In such circumstances hiding & showing views in single ViewController is really not the smart way. So, we need to separate the views. This sample, I have created multiple ViewController's and to present on a single page. Here, I will show you to present multiple view controllers in a single page.

iOS output


Prerequisites 

  • Visual Studio 2019 
  • XCode 10.0 or above
  • Physical device or simulator 
  • This project created in Visual Studio 2019 for Mac

Let's start

Step 1:

Create Xamarin iOS  Single View Application by navigating to Visual Studio File Menu  >> New Project >> In the dialog window >> select left plane App under iOS and center plane select Single View Application and click Next.



Step 2:

In the next window, give your application a name, target version & device type (tab or mobile), organize & bundle identifier and click OK.


Step 3:

After the project creation, you will get a folder structure like below.



Now, open storyboard in you interface builder using the right click of Main.Storyboard >> in the context menu select Open with >> forwarded by XCode Interface Builder.


Step 4:

It's time to design our interface. Default one view controller will be there, We should place Navigation Controller in-front of Initial View Controller. Drag and drop the UICollectionView and View.  Here, collection view for creating a tabbed  page and view for set another controller view to this view. Because there is no default control for the tabbed page and set this collection view height and constraints as per your requirements. Create an outlet for CollectionView and View as  collectionView and baseView


Next, select collection view and open properties plane and set the size is None.



Add First View Controller 

Next, drag and drop the new view controller and set the background color as red and add label view in the center of the page. Create a new ViewController class named as FirstViewController to assign this ViewController.




Add Second View Controller

Similarly, create another view controller and set the background color as yellow and place one label in the center of the page. As well as create another view controller class named SecondViewController to assign to this View Controller.



finally, your storyboard looks like below



Step 5:

Now, add new Collection View Cell for tab design using right-click the solution >> add >> new File >> new dialog window will appear, in the left plane select iOS,>> center plane, select Collection View Cell and give the name as TabCollectionViewCell and then click Add.


Design Collection View Cell

Next, open this Collection View Cell in an Interface builder. Add the following views in the collection view cell.
  • Label - Tab title
  • View - Active tab indicator
The label in the center of the cell and view height 5. Set the perfect constraint for this view. If the constraint are missing, no view will be present in the view controller. Create an outlet for this view to access from the code behind.



There is one problem to apply data to view, you couldn't access this outlet's from any other class, so we can create one method to update data for views


internal void UpdateCell(string title, bool visible)
        {
            titleLabel.Text = title;
            titleLabel.TextColor = UIColor.White;

            if (visible)
                indicatorline.Hidden = false;
            else
                indicatorline.Hidden = true;
        }


Step 6:

Next moving into the coding part. First, we are going to write a code for the collection view. The view needs CollectionView needs collection view source and delegate class. The collection view there is no way to give input directly, so we need to write source class and pass your data to this class. The delegate class for cell item sizing and handle the item click event. Afterward, apply these two classes to this collection view.

Add Collection View Source Class

First, create a source class named CollectionViewSource and this class should be inherited from UICollectionViewSource and pass the data through the contractor. This page code is given below.


public class CollectionViewSource : UICollectionViewSource
    {
        private List<string> titles;
        public static int selectedIndex;

        public CollectionViewSource(List<string> titles, int Index)
        {
            this.titles = titles;
            selectedIndex = Index;
        }
        public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
        {
            var cell = collectionView.DequeueReusableCell("TabCollectionViewCell", indexPath) as TabCollectionViewCell;

            if (indexPath.Row == selectedIndex)
                cell.UpdateCell(this.titles[indexPath.Row], true);
            else
                cell.UpdateCell(this.titles[indexPath.Row], false);

            return cell;
        }
        public override nint GetItemsCount(UICollectionView collectionView, nint section)
        {
            return titles.Count;
        }
    }


Add the Collection View Delegate Class

Create another delegate class named CollectionViewSourceDelegate and this class should be inherited from UICollectionViewDelegateFlowLayout. In this class, constructor is passing one ITab interface to the view controller.

public class CollectionViewSourceDelegate : UICollectionViewDelegateFlowLayout
    {
        int listCount;
        ITab iTab;
        public CollectionViewSourceDelegate(int count, ITab iTab)
        {
            this.listCount = count;
            this.iTab = iTab;
        }
        public override nfloat GetMinimumInteritemSpacingForSection(UICollectionView collectionView, UICollectionViewLayout layout, nint section)
        {
            return 0;
        }
        public override CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
        {
            var size = new CGSize();
            size.Width = collectionView.Frame.Width / 2;
            size.Height = collectionView.Frame.Height;
            return size;
        }
        public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
        {
            iTab.OnTabChange(indexPath.Row);
            CollectionViewSource.selectedIndex = indexPath.Row;
            collectionView.ReloadData();
        }
    }

Create ITab interface

Create a new Interface named ITab.cs by going to Solution Explorer >> Add new file >>  select Interface and give the name as ITab and click Add. Add one method OntabChange and one integer parameter value.

public interface ITab
    {
        void OnTabChange(int indexPath);
    }

Next, open ViewController.cs file. First, we need to setup view controllers and present them into view. Add the FirstViewController and SecondViewController as Child View Controller of this class and set the bounds equal of baseView view. Load the view controller based on Index selection. In the OnTabMethod  call the LoadViewMethod and pass the index. create a static string list and pass to collection view source class.

using Foundation;
using System;
using System.Collections.Generic;
using UIKit;

namespace ManageChildVC
{
    public partial class ViewController : UIViewController, ITab
    {
        private FirstChildViewController firstController;
        private SecondChildViewController secondController;

        public ViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            // Perform any additional setup after loading the view, typically from a nib.

            var tabTitles = new List<string>() { "Tab 1 💝", "Tab 2 👨🏻‍💻" };

            SetUpChildViewControllers();

            collectionView.RegisterNibForCell(TabCollectionViewCell.Nib, "TabCollectionViewCell");
            collectionView.Source = new CollectionViewSource(tabTitles, 0);
            collectionView.Delegate = new CollectionViewSourceDelegate(tabTitles.Count, this);
            collectionView.ReloadData();

            LoadController(0);
        }

        private void SetUpChildViewControllers()
        {
            AddViewControllerAsChildViewController(FirstTabViewController());
            AddViewControllerAsChildViewController(SecondTabViewController());
        }
        public UIViewController FirstTabViewController()
        {
            firstController = this.Storyboard.InstantiateViewController("FirstChildViewController") as FirstChildViewController;
            return firstController;
        }
        public UIViewController SecondTabViewController()
        {
            secondController = this.Storyboard.InstantiateViewController("SecondChildViewController") as SecondChildViewController;
            return secondController;
        }

        private void AddViewControllerAsChildViewController(UIViewController viewController)
        {
            this.AddChildViewController(viewController);
            View.AddSubview(viewController.View);
            viewController.View.Frame = ViewControllerSpace.Frame;
            viewController.DidMoveToParentViewController(this);
            viewController.View.Hidden = true;
        }

        public override void DidReceiveMemoryWarning()
        {
            base.DidReceiveMemoryWarning();
            // Release any cached data, images, etc that aren't in use.
        }

        public void OnTabChange(int indexPath)
        {
            LoadController(indexPath);
        }

        private void LoadController(int indexPath)
        {
            collectionView.ReloadData();

            if (indexPath == 0)
            {
                firstController.View.Hidden = false;
                secondController.View.Hidden = true;
            }
            else if (indexPath == 1)
            {
                firstController.View.Hidden = true;
                secondController.View.Hidden = false;
            }
        }
    }
}


Step 7:

Now, run the application, you will get a view like below, select the tabs, based on tab selection the view will change.


The full source code is here -     

Conclusion

In this blog, we learned to handle the view controllers in iOS. Thanks for reading and share your comments.

No comments:

Post a Comment

You're comment here