Thursday, April 26, 2012

Supplying separate iPad and iPhone views in a single Universal iOS MvvmCross project (MonoTouch)


The default convention used by the MvvmCross platform is to automatically register all views using reflection.
    protected virtual void InitializeViews()
    {
        var container = this.GetService();

        foreach (var pair in GetViewModelViewLookup())
        {
            Add(container, pair.Key, pair.Value);
        }
    }
where GetViewModelViewLookup returns a dictionary of ViewModel type to View type:
    protected virtual IDictionary GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
    {
        var views = from type in assembly.GetTypes()
                    let viewModelType = GetViewModelTypeMappingIfPresent(type, expectedInterfaceType)
                    where viewModelType != null
                    select new { type, viewModelType };

        return views.ToDictionary(x => x.viewModelType, x => x.type);
    }
In universal iPad/iPhone apps you do occasionally want to include multiple views for each viewmodel - using one view in the iPad and one view in the iPhone.
To do this, there are now (literally just now!) some attributes available to mark your views as being "unconventional" - these are:
The last of these is probably what you want in this case - you could implement simple iPhone/iPad switching for a MainViewModel using two views declared like:
[MvxFormFactorSpecificView(MvxTouchFormFactor.Phone)]
public class MyIPhoneView : BaseView
{
    // iphone specific view ...
}

[MvxFormFactorSpecificView(MvxTouchFormFactor.Pad)]
public class MyIPadView : BaseView
{
    // ipad specific view ...
}

Alternatively if you want a very custom configuration, you can override all 'convention-based' behaviour - you can implement your own override of GetViewModelViewLookup - e.g.:
protected override IDictionary GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
{
    if (IsIPad)
    {
        return new Dictionary() 
        {
            { typeof(HomeViewModel), typeof(IPadHomeView) },
            { typeof(DetailViewModel), typeof(IPadDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
    else
    {
        return new Dictionary() 
        {
            { typeof(HomeViewModel), typeof(IPhoneHomeView) },
            { typeof(DetailViewModel), typeof(IPhoneDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
}

Note that eventually you may decide that you need additional ViewModels as well as Views for the iPad app - the iPad has, after all, a much bigger screen - in this case you can add them manually. Ultimately, when your app hits a few million users, you may even decide to completely branch the tablet code away from the phone code - but that can generally wait until you hit that few million mark...

No comments:

Post a Comment