Xamarin.iOS 中的 Storyboard

在本指南中,我们介绍什么是 Storyboard,并讲解一些关键组件(例如 Segue)。 我们将探讨如何创建和使用 Storyboard,以及它们为开发人员带来哪些优势。

在 Apple 引入 Storyboard 文件格式作为 iOS 应用程序 UI 的可视表示形式之前,开发人员为每个视图控制器创建了 XIB 文件,并手动编程每个视图之间的导航。 使用 Storyboard,开发人员可以在设计图面上定义视图控制器和它们之间的导航,并对应用程序的用户界面提供 WYSIWYG 编辑。

可以使用 Visual Studio for Mac 创建和打开 Storyboard。 本指南还将演练如何在使用 C# 对导航进行编程时使用 Xcode Interface Builder 创建 Storyboard。

要求

Storyboard 可与 Xcode 一起使用,并从 Visual Studio for Mac 上的 Xamarin.iOS 项目中启动。

什么是 Storyboard?

Storyboard 是应用程序中所有屏幕的可视表示形式。 它包含一系列场景,每个场景表示一个视图控制器及其视图。 这些视图可能包含允许用户与应用程序进行交互的对象和控件。 由视图和控件(也称为“子视图”)组成的集合称为内容视图层次结构。 场景由 segue 对象连接,这些对象表示视图控制器之间的过渡。 这通常是通过在初始视图和连接视图上的对象之间创建 segue 来实现的。 下图演示了设计图面上的关系:

展示了设计图面上的关系

如图所示,Storyboard 展示了你的每个场景和已经呈现的内容,并说明了它们之间的连接。 当我们谈论 iPhone 上的场景时,可以安全地假设 Storyboard 上的一个场景等于设备上的一个内容屏幕。 但是,在 iPad 上,可以同时出现多个场景,例如使用 Popover 视图控制器。

使用 Storyboard 创建应用程序的 UI 有很多优点,尤其是在使用 Xamarin 时。 首先,它是 UI 的可视表示形式,因为设计时呈现所有对象(包括自定义控件)。 这意味着,在生成或部署应用程序之前,可以可视化其外观和流。 以前面的图像为例。 我们可以通过快速浏览设计图面来了解有多少个场景、每个视图的布局,以及所有内容是如何关联的。 这就是 Storyboard 如此强大的原因。

还可以使用 Storyboard 来管理事件。 大多数 UI 控件在 Properties Pad 中都有一个可能事件的列表。 可以在这里添加事件处理程序,并在视图控制器类的分部方法中完成。

Storyboard 的内容存储为 XML 文件。 在生成时,任何 .storyboard 文件都被编译为称为 nibs 的二进制文件。 在运行时,将初始化和实例化这些 nibs 以创建新视图。

Segue

在 iOS 开发中使用 Segue 或 Segue 对象来表示场景之间的过渡。 若要创建 Segue,请按下 Ctrl 键,然后从一个场景单击拖动到另一个场景。 拖动鼠标时,将显示一个蓝色连接线,指示 Segue 将指向何处。 如下图所示:

此时会显示蓝色连接器,指示 segue 将引导至何处,如下图所示

当鼠标向上移动时,将显示一个菜单,用于选择 Segue 的操作。 它可能看起来与下图类似:

低于 iOS 8 的版本和 Size 类

不带大小类的“操作 Segue”下拉列表

使用 Size 类和自适应 Segue 时

包含大小类的“操作 Segue”下拉列表

重要

如果对 Windows 虚拟机使用 VMWare,则 Ctrl-单击默认情况下映射到“右键单击”鼠标按钮。 若要创建 Segue,请通过“首选项”>“键盘和鼠标”>“鼠标快捷方式”来编辑键盘首选项,并重新映射辅助按钮,如下所示:

键盘和鼠标首选项设置

现在,你应该能够像平常一样在视图控制器之间添加 Segue。

有不同类型的过渡,每个过渡控制如何向用户显示一个新的视图控制器,以及它如何与 Storyboard 中的其他视图控制器交互。 这些在下文中说明。 还可以将 Segue 对象子类化来实现自定义过渡:

  • 显示/推送 - 推送 segue 会将视图控制器添加到导航堆栈。 它假定发起推送的视图控制器与添加到堆栈的视图控制器属于同一导航控制器。 它的作用与 pushViewController 相同,并且通常在屏幕上的数据之间存在某种关系时使用。 使用推送 Segue 可以让你拥有一个导航栏,它带有后退按钮和标题,添加到堆栈上的每个视图,允许在视图层次结构中向下钻取导航。

  • 模式 - 模式 segue 在项目中的任何两个视图控制器之间创建关系,并提供显示动画过渡的选项。 当子视图控制器被引入视图时,它将完全掩盖父视图控制器。 与添加后退按钮的推送 Segue 不同,在使用模式 segue 时必须使用 DismissViewController 才能返回到上一个视图控制器。

  • 自定义 - 任何自定义 Segue 都可以创建为 UIStoryboardSegue 的子类。

  • 展开 - 展开 Segue 可用于通过推送或模式 Segue 导航回去,例如通过消除模式呈现的视图控制器。 除此之外,还可通过一系列推送和模式 Segue 进行展开,并在导航层次结构中使用单个展开操作后退多个步骤。 若要了解如何在 iOS 中使用展开 Segue,请阅读创建展开 Segue 手册。

  • 无源 - 无源 Segue 指示包含初始视图控制器的场景,进而指示用户将首先看到哪个视图。 它由下面的 Segue 表示:

    无源 segue

自适应 Segue 类型

iOS 8 引入了 Size 类,这些类支持 iOS Storyboard 文件适应所有可用的屏幕大小,让开发人员能够为所有 iOS 设备创建一个 UI。 默认情况下,所有新的 Xamarin.iOS 应用程序都使用 size 类。 若要使用较旧项目中的 size 类,请参阅统一 Storyboard 简介指南。

任何使用 size 类的应用程序也将使用新的自适应 Segue 使用 size 类时,请记住,你不会直接指定使用的是 iPhone 还是 iPad。 换句话说,你创建的 UI 看起来总是一样的,不管它需要多大的空间。 自适应 Segue 通过判断环境并确定如何最好地呈现内容来工作。 自适应 Segue 如下所示:

“自适应 Segues”下拉列表

Segue 说明
显示 这与推送 Segue 非常相似,但它考虑到了屏幕的内容。
显示详细信息 如果应用显示主视图和细节视图(例如在 iPad 上的拆分视图控制器中),内容将替换细节视图。 如果应用仅显示主视图或细节视图,则内容将替换视图控制器堆栈的顶部。
呈现 这与模式 Segue 非常类似,并支持选择显示样式和过渡样式。
Popover 显示 这将内容显示为 Popover。

使用 Segue 传输数据

Segue 的好处不止于过渡。 它们还可用于管理视图控制器之间的数据传输。 这是通过在初始视图控制器上重写 PrepareForSegue 方法并自行处理数据来实现的。 触发 Segue(例如按下按钮)时,应用程序将调用此方法,这使得有机会在发生任何导航之前提供准备新的视图控制器。 以下代码对此做了演示:

public override void PrepareForSegue (UIStoryboardSegue segue,
NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    var callHistoryController = segue.DestinationViewController
                                  as CallHistoryController;

    if (callHistoryController != null) {
        callHistoryController.PhoneNumbers = PhoneNumbers;
    }
}

在此示例中,当用户触发 Segue 时,会调用 PrepareForSegue。 必须先创建“接收”视图控制器的实例,并将其设置为 Segue 的目标视图控制器。 此操作由以下代码行完成:

var callHistoryController = segue.DestinationViewController as CallHistoryController;

该方法现在可以在 DestinationViewController 上设置属性。 此示例利用了这种功能,它将名为 PhoneNumbers 的列表传递到 CallHistoryController 并将其分配给同名对象:

if (callHistoryController != null) {
        callHistoryController.PhoneNumbers = PhoneNumbers;
    }

过渡完成后,用户会看到 CallHistoryController 和已填充的列表。

将 Storyboard 添加到非 Storyboard 项目

有时,可能需要将 Storyboard 添加到以前的非 Storyboard 文件中。 可按照以下步骤在 Visual Studio for Mac 中简化该过程:

  1. 浏览到“文件”>“新建文件”>“iOS”>“Storyboard”来创建新的 Storyboard 文件。

    “新建文件”对话框

  2. 将 Storyboard 名称添加到 Info.plist 的“主要界面”部分。

    Info.plist 编辑器

    这相当于在应用委托内的 FinishedLaunching 方法中实例化初始视图控制器。 设置此选项后,应用程序将实例化窗口(见下一步),加载主要 Storyboard,并将 Storyboard 初始视图控制器的实例(无源 Segue 旁边的实例)指定为窗口的 RootViewController 属性。 然后,它使窗口在屏幕上可见。

  3. AppDelegate 中,使用以下代码重写默认的 Window 方法来实现 window 属性:

    public override UIWindow Window {
        get;
        set;
    }
    

使用 Xcode 创建 Storyboard

可使用 Xcode 创建和修改 Storyboard,以便在使用 Visual Studio for Mac 开发的 iOS 应用中使用。

Storyboard 完全取代项目中的各个 XIB 文件,但仍可使用 Storyboard.InstantiateViewController 将 Storyboard 中的单个视图控制器实例化。

有时,应用程序具有设计器提供的内置 Storyboard 过渡无法处理的特殊要求。 例如,如果要创建从同一按钮启动不同屏幕的应用程序,那么根据应用程序的当前状态,可能需要手动实例化视图控制器,并自行对过渡进行编程。

以下屏幕截图显示了设计图面上的两个视图控制器,控制器之间没有 Segue。 下一部分将讲解如何在代码中设置过渡。

  1. 将空的 iPhone Storyboard 添加到现有项目:

    添加情节提要

  2. 双击 Storyboard 文件,或者右键单击并选择“使用 > Xcode Interface Builder 打开”,在 Xcode 的 Interface Builder 中打开它。

  3. 在 Xcode 中,打开库(通过“视图”>“显示库”或 Shift + Command + L)来显示可添加到 Storyboard 的对象列表。 通过将列表中的对象拖动到 Storyboard,向 Storyboard 添加一个 Navigation Controller。 默认情况下,Navigation Controller 将提供两个屏幕。 右侧的屏幕是一个 TableViewController,你要将它替换为更简单的视图,以便可通过单击视图和按 Delete 键来删除它。

    从库中添加 NavigationController

  4. 此视图控制器将有自己的自定义类,并且还需要自己的 Storyboard ID。 单击这个新添加的视图上方的框时,有三个图标,最左侧的图标表示视图的视图控制器。 通过选择此图标,可以在右窗格的标识选项卡上设置类和 ID 值。将这些值设置为 MainViewController,并确保检查 Use Storyboard ID

    在标识面板中设置 MainViewController

  5. 再次使用库,将视图控制器控件拖到屏幕上。 这会设置为根视图控制器。 按住 Ctrl 键,单击左侧导航控制器并将其拖动到右侧新添加的视图控制器,然后在菜单中选择“根视图控制器”。

    从库中添加 NavigationController,并将 MainViewController 设置为根视图控制器

  6. 此应用将导航到另一个视图,因此请像以前一样再向 Storyboard 添加一个视图。 将其命名为 PinkViewController,并以与 MainViewController 相同的方式设置这些值。

    屏幕截图显示了具有三个视图的情节提要。

  7. 由于视图控制器将有粉红色背景,请使用 Background 旁边的下拉列表在特性面板中设置该属性。

    屏幕截图显示了上一步中的情节提要,最右侧的屏幕已更改为粉红色背景。

  8. 我们希望 MainViewController 导航到 PinkViewController,因此前者需要一个按钮来与之交互。 使用库将按钮添加到 MainViewController

    向 MainViewController 添加按钮

Storyboard 已完成,但如果现在部署项目,你将看到一个空白屏幕。 这是因为你仍然需要告诉 IDE 使用 Storyboard,并设置根视图控制器来充当第一个视图。 通常可以通过项目选项完成此操作,如上所示。 但是,在此示例中,我们将以下代码添加到 AppDelegate 来实现相同的结果:

public partial class AppDelegate : UIApplicationDelegate
{
    UIWindow window;
    public static UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null);
    public static UIViewController initialViewController;

    public override bool FinishedLaunching (UIApplication app, NSDictionary options)
    {
        window = new UIWindow (UIScreen.MainScreen.Bounds);

        initialViewController = Storyboard.InstantiateInitialViewController () as UIViewController;

        window.RootViewController = initialViewController;
        window.AddSubview(initialViewController.View);
        window.MakeKeyAndVisible ();
        return true;
    }
}

这有很多代码,但只有几行是不熟悉的。 首先,通过传入 Storyboard 的名称“MainStoryboard”,将 Storyboard 注册到 AppDelegate。 接下来,指示应用程序通过在 Storyboard 上调用 InstantiateInitialViewController 来从 Storyboard 中实例化一个初始视图控制器,并将该视图控制器设置为应用程序的根视图控制器。 此方法确定用户看到的第一个屏幕,并创建该视图控制器的新实例。

请注意,在“解决方案”窗格中,当你在步骤 4 中将类名添加到 Properties Pad 时,IDE 创建了一个 MainViewcontroller.cs 类及其相应的 *.designer.cs 文件。 此类创建了一个包含基类的特殊构造函数:

public MainViewController (IntPtr handle) : base (handle)
{
}

使用 Xcode 创建 Storyboard 时,IDE 将自动在 *.designer.cs 类的顶部添加 [Register] 特性,并传入字符串标识符,该标识符与上一步中指定的 Storyboard ID 相同。 这会将 C# 链接到 Storyboard 中的相关场景。

[Register ("MainViewController")]
public partial class MainViewController : UIViewController
{
    public MainViewController (IntPtr handle) : base (handle)
    {
    }
    //...
}

有关注册类和方法的详细信息,请参阅 Registrar 类型

此类中的最后一步是将按钮和过渡连接到粉色视图控制器。 你将从 Storyboard 实例化 PinkViewController;然后,你将使用 PushViewController 对推送 Segue 进行编程,如以下示例代码所示:

public partial class MainViewController : UIViewController
{
    UIViewController pinkViewController;

    public MainViewController (IntPtr handle) : base (handle)
    {
    }

    public override void AwakeFromNib ()
    {
        // Called when loaded from xib or storyboard.
        this.Initialize ();
    }

    public void Initialize()
    {
        //Instantiating View Controller with Storyboard ID 'PinkViewController'
        pinkViewController = Storyboard.InstantiateViewController ("PinkViewController") as PinkViewController;
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        //When we push the button, we will push the pinkViewController onto our current Navigation Stack
        PinkButton.TouchUpInside += (o, e) =>
        {
            this.NavigationController.PushViewController (pinkViewController, true);
        };
    }
}

运行应用程序会生成一个双屏应用程序:

示例应用运行屏幕

条件 Segue

通常,从一个视图控制器移动到下一个视图控制器取决于特定条件。 例如,如果我们要创建简单的登录屏幕,那么我们希望只有当用户名和密码已验证时才移动到下一个屏幕。

在下一个示例中,我们将向前面的示例添加一个密码字段。 用户只有当输入正确的密码时才能访问 PinkViewController,否则将显示错误。

在开始之前,请按照前面的步骤 1-8 操作。 在这些步骤中,我们将创建 Storyboard,开始创建 UI,并指示应用委托将哪个视图控制器用作其 RootViewController。

  1. 现在,让我们构建 UI 并将列出的其他视图添加到 MainViewController,使其如以下屏幕截图中所示:

    • UITextField
      • 名称:PasswordTextField
      • 占位符:“输入机密密码”
    • UILabel
      • 文本:“错误:密码错误。 你不应该通过!”
      • 颜色:红色
      • 对齐方式:居中
      • 行数:2
      • 选中“已隐藏”复选框

    中线

  2. 通过控件从 PinkButton 拖动到 PinkViewController,然后在鼠标向上移动时选择“推送”,在“转到粉色”按钮和视图控制器之间创建 Segue。

  3. 单击 Segue 并为其指定标识符SegueToPink

    单击 Segue 并为其指定标识符 SegueToPink

  4. 最后,将下面的 ShouldPerformSegue 方法添加到 MainViewController 类:

    public override bool ShouldPerformSegue (string segueIdentifier, NSObject sender)
    {
    
        if(segueIdentifier == "SegueToPink"){
            if (PasswordTextField.Text == "password") {
                PasswordTextField.ResignFirstResponder ();
                return true;
            }
            else{
                ErrorLabel.Hidden = false;
                return false;
            }
        }
        return base.ShouldPerformSegue (segueIdentifier, sender);
    }
    

在此代码中,我们已将 segueIdentifier 与 SegueToPink Segue 匹配,因此我们可以测试条件;在本例中为有效密码。 如果我们的条件返回 true,Segue 将执行并显示 PinkViewController。 如果为 false,则不显示新的视图控制器。

通过检查 ShouldPerformSegue 方法的 segueIdentifier 参数,可以将此方法应用于此视图控制器上的任何 Segue。 在本例中,我们只有一个 Segue 标识符 - SegueToPink

总结

本文介绍了 Storyboard 的概念,以及它们如何在 iOS 应用程序开发中发挥作用。 它讨论了场景、视图控制器、视图和视图层次结构,介绍了场景如何与不同类型的 Segue 链接在一起。 它还探索如何从 Storyboard 手动实例化视图控制器以及如何创建条件 Segue。