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

Jedi Developer
  • 556
  • 4
  • 6

4 Answers4

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...

Stuart
  • 66,722
  • 7
  • 114
  • 165
  • 4
    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. – Mando Apr 22 '14 at 02:10
  • So - presumably this would involve a Storyboard without Segues defined. Any Segues that were defined would be ignored? – Jedi Developer May 30 '14 at 15:50
  • The second approach seems to be nice - but it is crashing on me during InstantiateViewCOntroller. Probably navigation will not be easily possible then anyways. – Obiwan007 Sep 08 '14 at 10:05
  • 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? – Marcos Lora Dec 22 '14 at 18:44
  • 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 help – Stuart Dec 22 '14 at 19:35
  • 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'. – rideintothesun Jan 28 '15 at 18:08
  • Hi, I have problems like Stuart and sisterray – Mykyta Bondarenko Mar 27 '15 at 11:46
  • See @kwl's answer. Storyboards are supported in MvvmCross now. – Cheesebaron Dec 14 '15 at 13:01
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.

Geir Sagberg
  • 9,632
  • 8
  • 45
  • 60
  • One could also make a container that support both Storyboards and Xibs. Maybe this could be added to the default container in MvvmCross? – Geir Sagberg Nov 12 '14 at 18:26
  • Make sure your view class inherits from MvxTouchView and that you have implemented the constructor in caveat 2 above. Otherwise no clue, sorry. – Geir Sagberg Dec 22 '14 at 18:48
  • 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 mess – Pat Long - Munkii Yebee Jan 22 '15 at 16:04
  • 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? – Mahadevan Sreenivasan Feb 18 '15 at 06:44
  • 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. – Geir Sagberg Feb 18 '15 at 06:48
  • @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 - http://stackoverflow.com/questions/28598392/monotouch-exception-not-getting-caught-on-device – Mahadevan Sreenivasan Feb 19 '15 at 04:12
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.

kwl
  • 495
  • 6
  • 13
  • 1
    Useful reference for setting name of storyboard in MvxFromStoryboard decorator: https://www.mvvmcross.com/documentation/platform/ios-user-interfaces-approaches#storyboards – Elijah Lofgren Dec 20 '17 at 16:40
3

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

nicruo
  • 508
  • 4
  • 13
  • your demo works. I tried to copy this sample and it seems that I did somethig wrong. Can you please help? https://github.com/slown1/Xamarin.iOS-Storyboard – Demian Flavius Aug 30 '15 at 09:48