MVVMCross support for Xamarin.iOS Storyboards
Asked Answered
G

4

26

With support for XS integration of iOS storyboards about to make the Stable stream, I would love to be able to use this feature in conjunction with MVVMCross.

Fundamentally it does seem a little like it should not work, as with storyboards indicate navigational hierarchy in the view project, rather than a viewmodel project like MVVMCross.

But it would be awesome if there is a way to make the 2 work together.

Does anyone know how this might be achieved?

Cheers, Tristan

Geography answered 2/3, 2014 at 11:25 Comment(0)
A
30

There is at least one sample published showing the use of Storyboards - the rather oddly named eh - https://github.com/slodge/eh

This sample worked by:

Using approaches like this it's pretty easy to add Mvx Data-Binding to an application that is primarily driven by the Storyboard.


Alternatively, if developers would prefer to let the Mvx ShowViewModel navigation system control the flow of screens - but would also prefer those screens to be designed within a storyboard, then this is possible by developing a normal MvvmCross application, but using a custom Presenter which loads ViewControllers from the storyboard.

In v3.1.1 of MvvmCross, you can do this at the ViewsContainer level:

  • override a class MyContainer from MvxTouchViewsContainer.cs
  • override the method protected virtual IMvxTouchView CreateViewOfType(Type viewType, MvxViewModelRequest request) - see https://github.com/MvvmCross/MvvmCross/blob/b8545752f28f4e569efeaa397c3085b0373e4d8b/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewsContainer.cs#L40
  • in this override, load your Storyboard-based ViewControllers:

     protected override IMvxTouchView CreateViewOfType(Type viewType, MvxViewModelRequest request)
     {
         return (IMvxTouchView)UIStoryboard.FromName("MyStoryBoard", null)
                                           .InstantiateViewController(viewType.Name);
     }
    
  • create your MyContainer during Setup -

    protected override IMvxTouchViewsContainer CreateTouchViewsContainer()
    {
        return new MyContainer();
    } 
    
  • that should just then work...

Aforesaid answered 2/3, 2014 at 14:25 Comment(8)
the second approach is definitely proper way to do that as long as we develop real cross-platform app. It is better to navigate using ShowViewModel and share this logic among the platforms.Ways
So - presumably this would involve a Storyboard without Segues defined. Any Segues that were defined would be ignored?Geography
The second approach seems to be nice - but it is crashing on me during InstantiateViewCOntroller. Probably navigation will not be easily possible then anyways.Multivocal
This approach fails in my case. The result is that it can't convert UIViewController to IMvxTouchView. It creates a generic UIViewController instance. Do you have any idea about why this is happening?Yasmin
Ask a good question - with the steps you've gone through and the necessary code for people to understand where it's going wrong for you - people will try to helpAforesaid
Hi I've followed the steps above but when the overloaded CreateViewOfType is called I get an InvalidCastException because I cannot cast UIKIt.UIViewController to IMvxTouchViewSystem.InvalidCastException: Unable to cast object of type 'UIKit.UIViewController' to type 'Cirrious.MvvmCross.Touch.Views.IMvxTouchView'.Solicit
Hi, I have problems like Stuart and sisterrayHopeless
See @kwl's answer. Storyboards are supported in MvvmCross now.Pibgorn
S
11

In a large project, keeping all views in a single storyboard may be daunting.

I prefer creating one storyboard per view; I modified the Container in Stuart's answer to look for a storyboard matching the view class, and fall back to the main storyboard if not found:

public class StoryBoardContainer : MvxTouchViewsContainer
{
    protected override IMvxTouchView CreateViewOfType(Type viewType, MvxViewModelRequest request)
    {
        UIStoryboard storyboard;
        try
        {
            storyboard = UIStoryboard.FromName(viewType.Name, null);
        }
        catch (Exception)
        {
            storyboard = UIStoryboard.FromName("StoryBoard", null);
        }
        return (IMvxTouchView) storyboard.InstantiateViewController(viewType.Name);
    }
}

Caveat 1: To instantiate viewcontrollers this way, you must set the Storyboard ID in the editor:

Storyboard ID

Caveat 2: Make sure your views inheriting MvxViewController have the constructor public MyView(IntPtr handle) : base(handle), as this is used to instantiate the view controllers from the storyboard.

Sangfroid answered 4/11, 2014 at 11:2 Comment(6)
One could also make a container that support both Storyboards and Xibs. Maybe this could be added to the default container in MvvmCross?Sangfroid
Make sure your view class inherits from MvxTouchView and that you have implemented the constructor in caveat 2 above. Otherwise no clue, sorry.Sangfroid
Has any one tried this approach and found that none of their AutoLayout Constraints were respected? I applied constraints via Designer that behave as expected, in the Designer but at runtime they are a complete messKeenan
Thanks for this. I am trying to use this approach. This works fine on the simulator. However, when running on the iOS Device, The Exception is not getting caught. The app simply crashes saying 'NSInvalidArgumentException : Could not find Storyboard named xxx' . Any idea why this happens only on the device?Iridic
I believe simulator is case insensitive regarding file names, while device is case sensitive; make sure you are using the correct case. Also avoid any exotic characters in the filename.Sangfroid
@GeirSagberg Thanks for that. I should have told you that I slightly modified the code you have written. I am working on an app which was previously developed without using Storyboards, i.e. all the views were created in code. Now I am planning to use storyboards for the new views. So I added a try-catch around (IMvxTouchView) storyboard.InstantiateViewController(viewType.Name) as well. Please refer to this thread where I have explained my problem in detail - #28598892Iridic
E
8

Storyboard support is now a part of MvvmCross. Use the one ViewController per Storyboard approach as described in Geir's answer, setting the Storyboard ID, and decorate your MvxViewController partial classes with [MvxFromStoryboard]. See sample code on my blog.

Elidiaelie answered 11/12, 2015 at 14:44 Comment(1)
Useful reference for setting name of storyboard in MvxFromStoryboard decorator: mvvmcross.com/documentation/platform/…Adessive
E
3

I've made a sample project (TipCalc) using mvvmcross and ios storyboard: https://github.com/nicruo/TipCalc.Storyboard

Encomium answered 26/4, 2015 at 13:35 Comment(1)
your demo works. I tried to copy this sample and it seems that I did somethig wrong. Can you please help? github.com/slown1/Xamarin.iOS-StoryboardMorgun

© 2022 - 2024 — McMap. All rights reserved.