MvvmCross Tabs 控制器包装到 UINavigation 控制器中?

Posted

技术标签:

【中文标题】MvvmCross Tabs 控制器包装到 UINavigation 控制器中?【英文标题】:MvvmCross Tabs Controller wrapped into UINavigation Controller? 【发布时间】:2017-07-27 08:13:58 【问题描述】:

我正在使用 Xamarin.ios 和 MvvmCross 5.x。我的根视图是选项卡控制器,我希望任何显示孩子的请求都会强制在选项卡外部(而不是内部)进行堆栈导航。

所以我将我的根视图定义如下:

[MvxFromStoryboard]
[MvxRootPresentation(WrapInNavigationController = true)]
public partial class HomeView : MvxTabBarViewController<HomeViewModel>

不幸的是,根导航控制器不是 UINavigationController(我期望基于属性)而是 HomeView,它本质上是选项卡视图,我无法实现我的目标。

然后我覆盖了 ios Presenter 来手动创建根 UINavigationController:

public class MySuperCoolIosViewPresenter : MvxIosViewPresenter

    private UINavigationController _rootViewController; 

    public MySuperCoolIosViewPresenter(IMvxApplicationDelegate applicationDelegate, UIWindow window)
        : base(applicationDelegate, window)
    
    

    protected override void SetWindowRootViewController(UIViewController controller)
    
        _rootViewController = new UINavigationController(controller);
        base.SetWindowRootViewController(_rootViewController);
    

    protected override void ShowChildViewController(UIViewController viewController, MvvmCross.iOS.Views.Presenters.Attributes.MvxChildPresentationAttribute attribute, MvxViewModelRequest request)
    
        _rootViewController.ShowViewController(viewController, _rootViewController);
    

但是在尝试设置控制器base.SetWindowRootViewController(_rootViewController) 时,此代码在 MvvmCross 深处的某处失败,并出现以下错误:

System.NullReferenceException: Object reference not set to an instance of an object\n  
at MvvmCross.iOS.Views.Presenters.MvxIosViewPresenter.CloseTabBarViewController () [0x00036] in <861dee92d7924acc93d876339b4b95f9>:0  
at MvvmCross.iOS.Views.MvxTabBarViewController.ViewWillDisappear (System.Boolean animated) [0x0001f] in <861dee92d7924acc93d876339b4b95f9>:0 
at (wrapper managed-to-native) ObjCRuntime.Messaging:void_objc_msgSend_IntPtr (intptr,intptr,intptr)  
at UIKit.UIWindow.set_RootViewController (UIKit.UIViewController value)

如何在不为根视图创建额外的 ViewModel 和 View 的情况下实现我的目标(我不喜欢这种方法,因为在这种情况下 android 会崩溃)

【问题讨论】:

【参考方案1】:

我创建了这个示例,其中包含您所需要的内容:https://github.com/rrispoli/SampleTabs

【讨论】:

感谢您的努力,该应用程序完全符合我的要求。不幸的是,您正在使用 mvvmcross 已经涵盖的一些手动选项卡创建逻辑(我想坚持该逻辑)和我不清楚的自定义 ios 演示器技巧:1. 为什么在ShowRootViewController 中您不调用基类方法? 2. 为什么用同样的方法调用SetWindowRootViewController? 3. 为什么在ShowChildViewController 中你必须设置TabBarViewController (基本上每次你想向孩子展示你的演示者都这样做)。请澄清 哦...我打开了原始的 mvvmcross 代码并得到了答案——代码取自那里:) 我已经测试了该解决方案,当 IMvxNavigationService 用于通过简单调用 Navigate 方法创建选项卡时,我无法将其用于通过 MvvmCross 创建选项卡 我不明白你到底想要什么。使用此代码,您不需要预先实例化视图模型,而是让 MvvmCross 为您实例化:github.com/rrispoli/SampleTabs/blob/master/SampleTabs.iOS/Views/… 您只需更改 CreateTabFor 签名以接受代表您需要的 viewModelType 的 Type 参数。然后,调用方法: CreateTabFor(0, "My tab", null, typeof(ChildViewModel)); @DominikR 我会尽快用新版本的 MvvmCross 更新存储库代码。【参考方案2】:

当我在寻找解决方案时,来自 MvvmCross 的家伙更新了框架以准确解决问题到版本 5.1.1(我使用的是 5.1.0)。 Ios 演示者逻辑已更新,正确地尊重 WrapInNavigationController(之前在选项卡的情况下被忽略)。不幸的是,ShowChildViewControllerInvalidCastException 之后失败了,但我能够通过将根导航控制器优先于选项卡控制器来处理这个问题

    protected override void ShowChildViewController(UIViewController viewController, MvxChildPresentationAttribute attribute, MvxViewModelRequest request)
    
        if (viewController is IMvxSplitViewController)
            throw new MvxException("A SplitViewController cannot be presented as a child. Consider using Root instead");

        if (ModalViewControllers.Any())
        
            if (ModalViewControllers.LastOrDefault() is UINavigationController modalNavController)
            
                PushViewControllerIntoStack(modalNavController, viewController, attribute.Animated);
                return;
            

            throw new MvxException($"Trying to show View type: viewController.GetType().Name as child, but there is currently a plain modal view presented!");
        

        // this logic goes first for the root controller 
        if (MasterNavigationController != null)
        
            PushViewControllerIntoStack(MasterNavigationController, viewController, attribute.Animated);
            return;
        

        // then try to show child for the tabs
        if (TabBarViewController != null && TabBarViewController.ShowChildView(viewController))
        
            return;
        

        base.ShowChildViewController(viewController, attribute, request);
    

我希望它能节省一些人的时间。

【讨论】:

您应该在 TabBarController 中覆盖 ShowChildView 并返回 false,以防您希望将视图显示为 MasterNavigationController 的子视图。对缺乏这方面的文档表示歉意,将简短地更新网站:) @nmilcoff 是的,即使没有自定义演示者,这种方法也可以工作,谢谢!目前还不清楚为什么 TabViewController 的 showChildView 给我一个无效的强制转换异常,是不是 5.1.1 的大? 确实是个bug,代码应该返回false而不是break。现在将修复它,感谢您告诉我!

以上是关于MvvmCross Tabs 控制器包装到 UINavigation 控制器中?的主要内容,如果未能解决你的问题,请参考以下文章

“Raw”Xamarin和MvvmCross

MvvmCross 4 和 Xamarin.iOS -> 使用 Storyboard 时如何从 Core 加载视图控制器?

Angular Material Tabs 不适用于包装器组件

Xamarin/Mvvmcross:收到 iOS 推送通知时打开不同的视图控制器

MvvmCross Android 片段添加到后台堆栈

注入默认字体