MVVMCross支持Xamarin.iOS Storyboards。

26

支持将 iOS storyboard 与 XS 集成的功能即将发布到 Stable 流,我希望能够结合 MVVMCross 使用此功能。

从根本上讲,它似乎有点不应该工作,因为 storyboard 表示视图项目中的导航层次结构,而不是像 MVVMCross 这样的 viewmodel 项目。

但如果有一种方法可以使这两个一起工作,那就太棒了。

有人知道如何实现吗?

谢谢,Tristan

4个回答

30
至少已经有一个使用Storyboards的示例发布了 - 这是一个名字相当奇怪的eh (https://github.com/slodge/eh)。

这个示例的工作方式:

使用这样的方法,向由Storyboard驱动的应用程序添加Mvx数据绑定非常容易。


或者,如果开发人员更喜欢使用Mvx ShowViewModel 导航系统控制屏幕流程 - 但也希望这些屏幕是在Storyboard中设计的,那么可以通过开发普通的MvvmCross应用程序,但使用一个自定义Presenter从Storyboard加载ViewControllers。

在MvvmCross v3.1.1中,您可以在ViewsContainer级别上执行此操作:

  • MvxTouchViewsContainer.cs覆盖类MyContainer
  • 覆盖方法protected virtual IMvxTouchView CreateViewOfType(Type viewType, MvxViewModelRequest request) - 参见https://github.com/MvvmCross/MvvmCross/blob/b8545752f28f4e569efeaa397c3085b0373e4d8b/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewsContainer.cs#L40
  • 在这个覆盖中,加载基于Storyboard的ViewControllers:

     protected override IMvxTouchView CreateViewOfType(Type viewType, MvxViewModelRequest request)
     {
         return (IMvxTouchView)UIStoryboard.FromName("MyStoryBoard", null)
                                           .InstantiateViewController(viewType.Name);
     }
    
  • Setup 过程中创建您的 MyContainer -

  • protected override IMvxTouchViewsContainer CreateTouchViewsContainer()
    {
        return new MyContainer();
    } 
    
  • 那应该只是能够正常工作...


4
只要我们开发真正的跨平台应用程序,第二种方法绝对是正确的方式。最好使用ShowViewModel进行导航,并在各个平台之间共享此逻辑。 - Mando
所以 - 可能这将涉及到一个没有定义 Segues 的 Storyboard。任何已定义的 Segues 将被忽略? - Jedi Developer
第二种方法看起来很不错 - 但是在InstantiateViewController期间它崩溃了。也许导航无论如何都不容易实现。 - Obiwan007
嗨,我有像Stuart和sisterray一样的问题。 - Mykyta Bondarenko
请参考@kwl的回答。现在MvvmCross支持Storyboard了。 - Cheesebaron
显示剩余3条评论

11
在一个大型项目中,将所有视图保留在单个Storyboard中可能会让人望而生畏。 我更喜欢为每个视图创建一个Storyboard;我修改了Stuart答案中的Container以查找与视图类匹配的Storyboard,如果没有找到,则回退到主Storyboard:
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);
    }
}

注意事项1: 要以这种方式实例化视图控制器,您必须在编辑器中设置Storyboard ID:

Storyboard ID

注意事项2:确保您的视图继承MvxViewController, 并且具有构造函数public MyView(IntPtr handle) : base(handle),因为这将用于从故事板实例化视图控制器。


也可以创建一个同时支持Storyboards和Xibs的容器。也许这可以添加到MvvmCross的默认容器中? - Geir Sagberg
请确保你的视图类从MvxTouchView继承,并且已经按照上述第2个警告中实现了构造函数。否则我就无能为力了,抱歉。 - Geir Sagberg
有人尝试过这种方法吗?发现没有一个自动布局约束被遵守了吗?我通过设计师应用了预期的约束,但在运行时它们变成了一团糟。 - Pat Long - Munkii Yebee
谢谢这个。我正在尝试使用这种方法。在模拟器上运行得很好。然而,在iOS设备上运行时,异常没有被捕获。应用程序崩溃并显示“NSInvalidArgumentException:找不到名为xxx的Storyboard”。有任何想法为什么只会在设备上发生这种情况? - Mahadevan Sreenivasan
我相信模拟器在文件名方面是不区分大小写的,而设备是区分大小写的;请确保您使用了正确的大小写。此外,请避免在文件名中使用任何奇异字符。 - Geir Sagberg
@GeirSagberg 谢谢你的回复。我应该告诉你,我稍微修改了你编写的代码。我正在开发一个之前没有使用Storyboards的应用程序,即所有视图都是通过代码创建的。现在我计划为新视图使用storyboards。因此,我还添加了一个try-catch语句来捕获(IMvxTouchView) storyboard.InstantiateViewController(viewType.Name)。请参考这个线程,我在其中详细解释了我的问题 - http://stackoverflow.com/questions/28598392/monotouch-exception-not-getting-caught-on-device - Mahadevan Sreenivasan

8

现在,故事板支持已成为MvvmCross的一部分。按照Geir的答案中所述,使用每个Storyboard一个ViewController的方法,设置Storyboard ID,并将你的MvxViewController部分类装饰上[MvxFromStoryboard]。请参阅我的博客上的示例代码。


1
在MvxFromStoryboard装饰器中设置故事板名称的有用参考:https://www.mvvmcross.com/documentation/platform/ios-user-interfaces-approaches#storyboards - Elijah Lofgren

3

你的演示很好用。我试着复制这个样例,但好像出了点问题。你能帮忙吗?https://github.com/slown1/Xamarin.iOS-Storyboard - Demian Flavius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接