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

Getting System.InvalidCastException while loading View from a xib file

我有一个 xib 文件 - LoaderView.xib 和关联的 class 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.

相关联

有人可以指出什么是显示加载、错误和空状态的最佳方式。我应该为他们单独设置 ViewController 吗?或者我可以从主视图控制器中 load/unload 获得单独的 xib 文件吗?如何从 xib 文件加载视图?

问题出在arr。

在上面的图片中,可以看出方法 LoadNibNamed 并没有每次都以相同的顺序返回 arr 中的顶级对象。这就是为什么代码 -

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

无法每次都将 arr 的第 0 项转换为键入 LoaderView

解决方案是遍历数组以找到您的类型的项目 - LoaderView 此处。对于我的情况 - 我写了一个静态 class 它有一个通用方法 LoadViewFromNib 其中 returns 类型 T 的视图继承自 NSView.

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));
    }
}

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

这是 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() 方法或其他方法class,因为这样您就可以调用私有初始化程序,如下所示:

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;
}