从xib文件加载View时获取System.InvalidCastException

时间:2018-02-20 05:45:12

标签: c# macos xamarin xib nsview

我有一个xib文件 - LoaderView.xib和相关的类LoaderView.cs,其中包含一个显示加载文本的视图。当我尝试将它作为 -

加载到主ViewController中时
public override void ViewDidAppear()
{
   base.ViewDidAppear();

   NSBundle.MainBundle.LoadNibNamed("LoaderView", this, out NSArray arr);
   var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
   view.Frame = View.Frame;
   View.AddSubview(view);
}

我在此行中收到System.InvalidCastException -

var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));

奇怪的是我每次都没有得到例外。当它工作时,我看到加载文本与默认视图重叠 - 与ViewController相关联。

enter image description here

有人可以指出显示加载,错误和空状态的最佳方式是什么。我应该为它们分别使用ViewControllers吗?或者我可以使用单独的xib文件从主视图控制器加载/卸载吗?如何从xib文件加载视图?

2 个答案:

答案 0 :(得分:1)

问题在于arr。

enter image description here

enter image description here

在上面的图像中,可以看出方法LoadNibNamed不是每次都以相同的顺序返回arr中的顶级对象。这就是代码 -

的原因
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));

每次都无法将arr的第0项投放到LoaderView类型。

解决方案是遍历数组以在此处找到您的类型的项目 - LoaderView。对于我的情况 - 我写了一个静态类,它有一个泛型方法LoadViewFromNib,它返回一个继承自NSView的类型为T的视图。

public static class LoadNib
{
    static NSArray xibItems;
    static nuint index;


    public static T LoadViewFromNib<T>(string filename, NSObject owner) where T : NSView
    {
        NSBundle.MainBundle.LoadNibNamed(filename, owner, out xibItems);

        for (nuint i = 0; i < xibItems.Count; i++)
        {
            NSObject item = xibItems.GetItem<NSObject>(i);
            if (item is T)
            {
                index = i;
                break;
            }
        }

        return Runtime.GetNSObject<T>(xibItems.ValueAt(index));
    }
}

至于重叠的问题,我发现除非明确设置背景,否则子视图是透明的。

答案 1 :(得分:0)

这是Xamarin的文档问题。他们的文档说使用第0个索引,但现实情况是数组顺序不确定。

Anagh给出的答案是足够的,并且在技术上是最佳的,但是您可以通过使用Linq使其更简洁,并且也很适合扩展方法:

public static class NibLoadingExtension
{
    public static T LoadViewFromNib<T>(this T @null) where T : NSView
    {
        NSBundle.MainBundle.LoadNibNamed(typeof(T).Name, null, out var array);
        return NSArray.FromArray<NSObject>(array).OfType<T>().FirstOrDefault();
    }
}

不幸的是,由于没有实例就无法在C#中调用扩展方法,因此必须像这样使用它:

var loaderView = default(LoaderView).LoadViewFromNib();

还有一个参数需要支持向每个单独的视图类添加静态Create()方法或其他内容,因为这样您就可以调用私有的初始化方法,如下所示:

public static LoaderView Create(ExampleDependency dependency)
{
    var model = dependency ?? throw new ArgumentNullException(nameof(dependency));
    NSBundle.MainBundle.LoadNibNamed(nameof(LoaderView), null, out var array);
    var view = NSArray.FromArray<NSObject>(array).OfType<LoaderView>().FirstOrDefault();
    view.Initialize(model);
    return view;
}

private void Initialize(ExampleDependency dependency)
{
    _privateField1 = dependency.Value1;
    _privateField2 = dependency.Value2;
    _privateField3 = dependency.Value3;
}