Xamarin.iOS 中的 .xib 代码生成

Apple Interface Builder 工具 (“IB”) 可用于直观地设计用户界面。 IB 创建的接口定义保存在 .xib 文件中。 .xib 文件中的小组件和其他对象可能会获得“类标识”,该标识可以是自定义用户定义类型。 使用自定义类型可以自定义小组件的行为并编写自定义小组件。

这些用户类通常是 UI 控制器类的子类。 它们具有可连接到接口对象的出口(属性)和操作(事件)。 在运行时,将加载 IB。 此时,将创建对象,并动态连接到各种 UI 对象的出口和操作。 定义这些托管类时,必须定义所有操作和出口,以匹配 IB 期望的操作和出口。 Visual Studio for Mac 使用类似于 CodeBehind 的模型来简化代码。 Xcode 具有类似的 Objective-C 模型。 但是,Xamarin.iOS 代码生成模型和约定已经过调整,使其令 .NET 开发人员更熟悉。

.xib 文件和自定义类

除了使用 Cocoa Touch 中的现有类型之外,还可以在 .xib 文件中定义自定义类型。 也可以使用在其他 .xib 文件中定义的类型,或者完全在 C# 代码中进行定义。 目前,Interface Builder 不知道当前 .xib 文件之外定义的类型的详细信息,因此它不会列出它们或显示其自定义出口和操作。 计划在未来某个时候移除此限制。

可以使用 Interface Builder 的“类”选项卡中的“添加子类”命令在 .xib 文件中定义自定义类。 我们将这些类称为“CodeBehind”类。 如果 .xib 文件在项目中具有“.xib.designer.cs”对应文件,则 Visual Studio for Mac 将自动填充 .xib 中所有自定义类的分部类定义。 我们将这些分部类称为“设计器类”。

生成代码

对于任何生成操作是 Page{0}.xib 文件,其代码生成是通过 {0}.xib.designer.cs 文件的存在来启用的。 Visual Studio for Mac 在设计器文件中为可在 .xib 文件中找到的所有用户类生成分部类。 Visual Studio for Mac 为出口生成属性,并为操作生成分部方法。

.xib 文件更改并且 Visual Studio for Mac 重新获得焦点时,设计器文件会自动更新。 不建议对设计器文件进行更改,因为下次 Visual Studio for Mac 更新文件时将覆盖更改。

注册和命名空间

Visual Studio for Mac 使用项目的默认命名空间为设计器文件位置生成设计器类。 此行为与正常 .NET 项目命名空间生成一致。 设计器文件的命名空间使用项目的“默认命名空间”及其“.NET 命名策略”设置。 如果项目的默认命名空间发生更改,则重新生成的类将使用新命名空间。 重新生成后,你可能会发现分部类不再匹配。

若要使类可由 Objective-C 运行时发现,Visual Studio for Mac 会将 [Register (name)] 属性应用于该类。 尽管 Xamarin.iOS 会自动注册 NSObject 派生类,但它使用的是完全限定的 .NET 名称。 Visual Studio for Mac 应用的属性将替代 Xamarin.iOS 行为,以确保每个类都以 .xib 文件中使用的名称注册。。 为使用 IB 定义的所有自定义类手动添加属性,而无需使用 Visual Studio for Mac 生成设计器文件。 这样做会使托管类与预期的 Objective-C 类名匹配。

不能在多个 .xib 中定义类,否则它们将互相冲突。

非设计器类部件

设计器分部类不按原样使用。 出口是私有的,没有指定基类。 预计每个类在另一个文件中都有相应的“非设计器”类部件。 “非设计器”文件会设置基类,操作出口,并定义从本机代码实例化类所需的构造函数。 默认 .xib 模板具有“非设计器”类部件,但对于在 .xib 中定义的任何其他自定义类,则必须手动添加非设计器部件。

这种使用分部类的分离方式需要灵活性。 例如,多个 CodeBehind 类可以子类化一个共同的托管抽象类,该类将子类化由 IB 子类化的类。

将 CodeBehind 类放置在 {0}.xib.designer.cs 设计器文件旁的 {0}.xib.cs 文件中是一种常规做法。

生成的操作和出口

在分部设计器类中,Visual Studio for Mac 会生成与 IB 中定义的任何连接出口对应的属性,以及与任何连接操作对应的分部方法。

出口属性

设计器类包含与自定义类上定义的所有出口对应的属性。 这些属性会启用延迟绑定。 它们是 Xamarin.iOS 到 Objective C 桥的实现详细信息。 可将它们视为专用字段,仅供 CodeBehind 类使用。 通过将公共访问器添加到非设计器类部件中的字段来公开字段。

如果将输出口属性定义为具有一种 id 的类型(等效于 NSObject),则为了方便起见,设计器代码生成器当前会根据连接到该出口的对象确定最强的类型。 但是,将来的版本可能不支持此行为。 建议在定义自定义类时显式键入出口。

操作属性

设计器类包含与自定义类上定义的所有操作对应的分部方法。 这些方法没有实现。 分部方法有两个用途:

  1. 如果在非设计器类部件的类正文中键入 partial,则 Visual Studio for Mac 将提供自动完成所有未实现的分部方法的签名。
  2. 分部方法签名应用了一个属性,该属性会将其公开给 Objective-C 世界,以便它们可以作为相应的操作进行处理。

你可以忽略分部方法,并通过将属性应用于其他方法来实现该操作。 或者让它传递到基类。

如果将操作定义为具有一种 id 的发送方类型(等效于 NSObject),则设计器代码生成器当前会根据连接到该操作的对象确定最强的类型。 但是,将来的版本可能不支持此行为。 建议在定义自定义类时显式键入操作。

这些分部方法仅为 C# 创建,因为 CodeDOM 不支持分部方法。 它们不会为其他语言进行生成。

跨 XIB 类用法

有时,用户希望从多个 .xib 文件中引用同一类,例如使用选项卡控制器。 可以从另一个 .xib 文件中显式引用类定义,或在第二个 .xib 中再次定义相同的类名。

由于 Visual Studio for Mac 会单独处理 .xib 文件,后一种情况可能会产生问题。 Visual Studio for Mac 无法检测和合并重复定义。 当多个设计器文件中定义了同一分部类时,就可能会发生多次应用 Register 属性的冲突。 最新版本的 Visual Studio for Mac 会尝试解决该冲突,但可能无法始终按预期工作。 在将来,此行为可能会变得不受支持,相反,Visual Studio for Mac 将使项目中所有 .xib 文件和托管代码中定义的所有类型直接从所有 .xib 文件中可见。

类型解析

IB 中使用的类型为使用 Register 属性映射到 CLR 类型的 Objective-C 类型名称。 生成代码时,Visual Studio for Mac 将解析 CLR 类型,并将类型名称完全限定为 Objective-C 类型。 这些 Objective-C 类型由 Xamarin.iOS 核心包装。

代码生成器当前无法从用户代码或库中的 Objective-C 类型名称解析 CLR 类型。 在这种情况下,它会逐字输出类型名称。 它必须与 Objective-C 类型具有相同的名称才能正确解析 CLR 类型。 CLR 类型必须与使用该类型的代码位于同一命名空间中。 将来的代码生成器版本将考虑项目中的所有 Objective-C 类型。