Load idiom-specific Storyboard in Xamarin.iOS with MvvmCross
I was prompted to write this post about a universal storyboard layout in Xamarin.iOS by the issue at the MvvmCross repository. It turned out there was a question about displaying different Storyboards bound to one ViewModel, so I decided to describe my experience in solving this problem.
Apple’s recommended way of using SizeClass is not enough when we need entirely different UI/UX for the iPad and iPhone, and the most straightforward way to achieve it is to use different Storyboards with a single ViewModel.
This post will describe how to teach the MvvmCross framework to load a View Controller from an idiom-specific Storyboard using a simple Xamarin.iOS app that renders a different UI on the iPhone and iPad.
But what exactly is a Storyboard? According to the official Xamarin tutorial, a Storyboard is the visual representation of all the screens in an application. It contains a sequence of scenes, with each scene representing a ViewController and its Views.
In our example, we use a small ViewModel with a text property to demonstrate that bindings are still working:
And that’s all about the ViewModels, so let’s move on to the View part.
First of all, MvvmCross supports loading view controllers from Storyboards by applying MvxFromStoryboardAttribute
to the UIViewController
-based class:
We are going to extend this feature to load different Storyboards, using a name convention for the storyboard files:
{StoryboardName}.Phone.storyboard
for iPhone-specific Storyboards{StoryboardName}.Pad.storyboard
for iPad-specific Storyboards
The {StoryboardName}
is the name we pass to the MvxFromStoryboard
attribute.
Let’s create a first Storyboard and the ViewController for our demo app. In Rider IDE, it could be done by clicking on the project and selecting the Add menu. This one will be an iPhone Storyboard, so we will name it Storyboard.Phone.storyboard
.
Then, we add a needed ViewController, implement the UI, add constraints, and ensure that the ViewController file is created and outlets are bound.
To create the iPad-specific Storyboard, we can copy the existing Storyboard.Phone.storyboard, rename it to Storyboard.Pad.storyboard
, and update the UI accordingly.
The ViewController file will not be generated, as we already generated this file with iPhone Storyboard. In other words: we have a 2 to 1 relationship between the Storyboard files and the ViewController file.
Here is the source code of the ViewController class:
Please pay attention to the MvxFromStoryboard
attribute: it tells MvvmCross the name of the Storyboard from which to load the ViewController.
In the existing implementation, MvvmCross will load the Storyboard.storyboard file, but we need to load either Storyboard.Phone.storyboard, or Storyboard.Pad.storyboard.
To intercept the View creation process, we need a custom MvxIosViewsContainer
implementation:
This custom container checks the idiom of the current interface and tries to load the Storyboard according to the file naming convention we applied earlier. If there is no Storyboard for a specific idiom, the default Storyboard will be loaded.
And the last thing we need to do is to register the container in the Setup
class:
Finally, our demo app can present different UI on iPad and iPhone:
You can find the complete solution of the Storyboard’s demo app though the link below: