Tab navigation for Xamarin.iOS using MvvmCross

Pranav Khandelwal
Jul 11 · 5 min read

This post is a part of #XamarinUIJuly. Check out all the other posts here!


Tabs are an extremely common navigation pattern in mobile applications. Some of the most popular and widely used mobile application feature tab-based navigation (WhatsApp, Facebook, Instagram, Messenger to name a few)

Bottom tabs on the Facebook mobile app

This post will walkthrough how to implement tab-based navigation in Xamarin.iOS using MvvmCross, a robust cross-platform MVVM framework.

We will implement tabs similar to the one seen in the Facebook application.

Notes

  • This walkthrough will show how to setup a brand new MvvmCross project from scratch for tab navigation, however, specific details about setting up MvvmCross are beyond the scope of this walkthrough.
  • We want to be able to perform nested navigation within each tab

Step 1: Solution Setup

We will start by setting up our MvvmCross solution. In Visual Studio, through the new project wizard choose the Blank Native App (iOS, Android) template. Complete the wizard leaving everything else as is.

Once the new project is created, delete the portable class library project and replace it with a .Net Standard Library project. We will refer to this as the Core project.

Replace the PCL project with a .Net Standard Library

Finally, delete the StoryBoard file from the iOS project and clear out the Main Interface setting in the Info.plist file.

Step 2: MvvmCross Setup

Now that we have a nice clean solution setup, lets add MvvmCross. We will start by installing the MvvmCross Nuget package into our iOS project and the Core project.

Now that we have all the packages installed, let’s setup our app to use MvvmCross.

In the Core project, create two files App.cs and TabsViewModel.cs:

//TabsViewModel.cs
public class TabsViewModel : MvxViewModel
{
public TabsViewModel(){ }
}
//App.cs
public class App : MvxApplication
{
public App()
{
}

public override void Initialize()
{
base.Initialize();
//Tell MvvmCross to show this ViewModel first
RegisterAppStart<TabsViewModel>();
}
}

Now that we have that setup, we need to setup our iOS project. We will come back and modify our TabsViewModel later.


Modify the existing AppDelegate.cs file as below:

[Register("AppDelegate")]
public class AppDelegate : MvxApplicationDelegate<MvxIosSetup<App>, App>
{
}

Next, create a TabsViewController class that inherits from MvxTabBarViewController:

[MvxRootPresentation(WrapInNavigationController=false)]
public class TabsViewController : MvxTabBarViewController<TabsViewModel>
{
public TabsViewController()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
}
}

Notice that the ViewController is decorated with the following attribute:[MvxRootPresentation(WrapInNavigationController=false)].

We are now ready to start setting up our tabs!

Step 3: Setup Tabs

The Facebook app has 5 tabs; we need to create ViewModels to represent each one of these tabs. We will create the ViewModels as such:

  1. News Feed — NewsFeedViewModel
  2. Requests — RequestsViewModel
  3. Messages — MessagesViewModel
  4. Notification — NotificationsViewModel
  5. More — MoreViewModel
public class NewsFeedViewModel : MvxViewModel
{
}
public class RequestsViewModel : MvxViewModel
{
}
public class MessagesViewModel : MvxViewModel
{
}
public class NotificationsViewModel : MvxViewModel
{
}
public class MoreViewModel : MvxViewModel
{
}

Now that we have our ViewModels setup, we need to modify our TabsViewModel from earlier and expose a method to Setup these tabs. Modify the TabsViewModel to looks like below:

public class TabsViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public TabsViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public Task SetupTabs()
{
return Task.WhenAll(
_navigationService.Navigate(typeof(NewsFeedViewModel)),
_navigationService.Navigate(typeof(RequestsViewModel)),
_navigationService.Navigate(typeof(MessagesViewModel)),
_navigationService.Navigate(typeof(NotificationsViewModel)),
_navigationService.Navigate(typeof(MoreViewModel)
));
}
}

We have done a few things here:

  • We use the built-in dependency injection in MvvmCross to obtain a reference to the MvxNavigationService
  • We expose a method that calls Navigate on the MvxNavigationService for each of our individual Tabs

Thats all the code we need in the Core project. We can now move on to the iOS project and finish setting up our tabs


On iOS, implement a ViewController for each of the above ViewModels. Decorate each ViewController with an [MvxTabPresentation] attribute. Here is the NewsFeedViewController as an example:

[MvxTabPresentation(TabName = "News Feed", TabIconName = "unselected", TabSelectedIconName = "selected", WrapInNavigationController = true)]
public class NewsFeedViewController : MvxViewController<NewsFeedViewModel>
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.BackgroundColor = UIColor.Red;
NavigationItem.Title = "News Feed";
}
}

Note that the [MvxTabPresentation] attribute has it’s WrapInNavigationController property set to true. This wraps each tab ViewController in a UINavigationController, allowing nested navigation within each tab.

Now that all the individual ViewControllers for each tab are setup, we have just one last thing left to do to complete our iOS implementation. Remember that SetupTabs method we implemented in TabsViewModel? We need to call that from our TabsViewController.

Modify the TabsViewController as shown below:

[MvxRootPresentationAttribute(WrapInNavigationController = false)]
public class TabsViewController : MvxTabBarViewController<TabsViewModel>
{
private bool _tabsInitialized;
public TabsViewController()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
}
public override async void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (!_tabsInitialized) {
await ViewModel.SetupTabs();
_tabsInitialized = true;
}
}
}

We override the ViewWillAppear lifecycle method and call our SetupTabs method, making sure its only ever called once.

So what is going on behind the covers?
When we call SetupTabs from our ViewController, the request is routed to the built-in MvvmCross iOS View Presenter which tells the MvxTabBarViewController to create and add the tab to the tab bar.

Check out the implementation of ShowTabViewController method in the MvxIosViewPresenter.

Thats it! iOS is complete! Build and run your app to see the tabs!

Xamarin.iOS + MvvmCross + Tabs = 🔥 🔥 🔥

Part 2 Tab navigation for Xamarin.Android using MvvmCross coming soon!

Thanks for reading! Grab the code on GitHub.