在 iOS 设计器中使用表

情节提要是创建 iOS 应用程序的所见即所得方式,在 Mac 和 Windows 上的 Visual Studio 中均受支持。 有关情节提要的详细信息,请参阅情节提要简介文档。 情节提要还允许你编辑表中的单元格布局,从而可以简化使用表和单元格进行开发的工作

在 iOS 设计器中配置表视图的属性时,可以选择两种类型的单元格内容:动态或静态原型内容

动态原型内容

包含原型内容的 UITableView 通常用于显示数据列表,在其中,原型单元格(或多个单元格,因为你可以定义多个单元格)将重用于列表中的每一项。 单元格不需要实例化,它们是在 GetView 方法中通过调用其 UITableViewSourceDequeueReusableCell 方法获取的。

静态内容

包含静态内容的 UITableView 允许直接在设计界面上设计表。 可以将单元格拖放到表中,并通过更改属性和添加控件来自定义单元格。

创建情节提要驱动的应用

StoryboardTable 示例包含一个简单的主从应用,该应用在情节提要中使用两种类型的 UITableView。 本部分的余下内部介绍如何生成一个小型待办事项列表示例,完成后,该列表如下所示:

Example screens

将使用情节提要生成用户界面,两个屏幕都将使用 UITableView。 主屏幕使用原型内容来排布行,详细信息屏幕使用静态内容来创建使用自定义单元格布局的数据输入表单

演练

在 Visual Studio 中使用“(创建新项目)新建项目...”>“单视图应用(C#)”创建新解决方案,并将其命名为 StoryboardTables

Create a new project dialog

该解决方案将打开,其中包含一些 C# 文件和已创建的 Main.storyboard 文件。 双击 Main.storyboard 文件以在 iOS 设计器中将其打开。

修改情节提要

使用三个步骤来编辑情节提要:

  • 首先,排布所需的视图控制器并设置其属性。
  • 接下来,通过将对象拖放到视图上来创建 UI
  • 最后,将所需的 UIKit 类添加到每个视图,并为各个控件命名,以便可以在代码中引用它们。

情节提要完成后,可以添加代码来使一切正常运行。

排布视图控制器

对情节提要的第一项更改是删除现有的详细信息视图并将其替换为 UITableViewController。 执行以下步骤:

  1. 选择视图控制器底部的栏并将其删除。

  2. 将工具箱中的“导航控制器”和“表视图控制器”拖放到情节提要上

  3. 创建从根视图控制器到刚刚添加的第二个表视图控制器的 segue。 若要创建 segue,请按住 Ctrl 键并从“详细信息”单元格拖动鼠标至新添加的 UITableViewController。 选择“Segue 选择”下的“显示”选项

  4. 选择创建的新 segue 并为其指定一个标识符,以在代码中引用此 segue。 单击 segue,然后在“Properties Pad”中输入 TaskSegue 作为标识符,如下所示:
    Naming segue in property panel

  5. 接下来,选择两个表视图并使用 Properties Pad 来配置它们。 请务必选择“视图”而不是“视图控制器”- 可以使用“文档大纲”来帮助做出选择。

  6. 将“根视图控制器”更改为“内容: 动态原型”(Design Surface 上的“视图”将标有“原型内容”)

    Setting the Content property to dynamic prototypes

  7. 将新的“UITableViewController”更改为“内容: 静态单元格”

  8. 必须为新的 UITableViewController 设置类名和标识符。 选择“视图控制器”,然后在“Properties Pad”中为“类”键入 TaskDetailViewController - 这会在 Solution Pad 中创建新的 TaskDetailViewController.cs 文件。 输入 StoryboardID 作为详细信息,如以下示例所示。 稍后将使用此信息在 C# 代码中加载此视图:

    Setting the Storyboard ID

  9. 情节提要设计界面现在应如下所示(根视图控制器的导航项标题已更改为“Chore Board”):

    Design surface

创建 UI

配置视图和 segue 后,接下来需要添加用户界面元素。

根视图控制器

首先,在主视图控制器中选择原型单元格,并将“标识符”设置为 taskcell,如下所示。 稍后将在代码中使用此标识符来检索此 UITableViewCell 的实例:

setting the cell identifier

接下来,需要创建一个用于添加新任务的按钮,如下所示:

bar button item in the navigation bar

请执行以下操作:

  • 将工具箱中的“栏按钮项”拖放到导航栏的右侧
  • 在“Properties Pad”中的“栏按钮项”下,选择“标识符: 添加”(使其变成 + 加号按钮)。
  • 为它命名,以便稍后可以在代码中识别它。 请注意,需要为根视图控制器提供一个类名(例如 ItemViewController),以便可以设置栏按钮项的名称

任务详细信息视图控制器

需要执行更多的操作来处理详细信息视图。 需要将表视图单元格拖放到视图上,然后在其中填充标签、文本视图和按钮。 以下屏幕截图显示了包含两个部分的已完成 UI。 第一个部分包含三个单元格、三个标签、两个文本字段和一个开关,而第二个部分包含一个单元格,该单元格包含两个按钮:

detail view layout

生成完整布局的步骤是:

选择表视图并打开“Properties Pad”。 更新以下属性:

  • 部分:2
  • 样式:分组
  • 分隔符:无
  • 选择:未选择

选择顶部部分,然后在“属性”>“表视图部分”下,将“行”更改为“3”,如下所示

setting the top section to three rows

对于每个单元格,请打开“Properties Pad”并设置

  • 样式:自定义
  • 标识符:为每个单元格选择唯一标识符(例如“title”、“notes”、“done”)
  • 拖动所需控件以生成屏幕截图中所示的布局(将 UILabel、UITextField 和 UISwitch 放置在正确的单元格上,并适当地设置标签,即 Title、Notes 和 Done)

在第二个部分,将“行”设置为“1”并抓住单元格底部的大小调整控点以增加其高度

  • 设置标识符:设置为唯一值(例如“save”)

  • 设置背景:透明颜色

  • 将两个按钮拖放到单元格上并适当地设置其标题(即 Save 和 Delete),如下所示

    setting two buttons in the bottom section

此时,你还可能想要对单元格和控件设置约束以确保获得自适应布局。

添加 UIKit 类并命名控件

还要完成几个最后步骤才能创建情节提要。 首先,我们必须在“标识”>“名称”下为每个控件指定一个名称,以便稍后可以在代码中使用它们。 如下所述为控件命名:

  • Title UITextField:TitleText
  • Notes UITextField:NotesText
  • UISwitch:DoneSwitch
  • Delete UIButton:DeleteButton
  • Save UIButton:SaveButton

添加代码

余下的工作将在 Mac 或 Windows 上的 Visual Studio 中使用 C# 来完成。 请注意,代码中使用的属性名称反映了上面的演练中设置的属性名称。

首先,我们要创建一个 Chores 类,它将提供一种获取和设置 ID、Name、Notes 和 Done 布尔值的方法,以便我们可以在整个应用程序中使用这些值。

Chores 类中添加以下代码:

public class Chores {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Notes { get; set; }
    public bool Done { get; set; }
  }

接下来,创建一个继承自 UITableViewSourceRootTableSource 类。

此视图与非情节提要表视图之间的差别在于,GetView 方法不需要实例化任何单元格 – theDequeueReusableCell 方法始终返回原型单元格的实例(具有匹配的标识符)。

以下代码摘自 RootTableSource.cs 文件:

public class RootTableSource : UITableViewSource
{
// there is NO database or storage of Tasks in this example, just an in-memory List<>
Chores[] tableItems;
string cellIdentifier = "taskcell"; // set in the Storyboard

    public RootTableSource(Chores[] items)
    {
        tableItems = items;
    }

public override nint RowsInSection(UITableView tableview, nint section)
{
  return tableItems.Length;
}

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  // in a Storyboard, Dequeue will ALWAYS return a cell, 
  var cell = tableView.DequeueReusableCell(cellIdentifier);
  // now set the properties as normal
  cell.TextLabel.Text = tableItems[indexPath.Row].Name;
  if (tableItems[indexPath.Row].Done)
    cell.Accessory = UITableViewCellAccessory.Checkmark;
  else
    cell.Accessory = UITableViewCellAccessory.None;
  return cell;
}
public Chores GetItem(int id)
{
  return tableItems[id];
}

若要使用 RootTableSource 类,请在 ItemViewController 的构造函数中创建新集合:

chores = new List<Chore> {
      new Chore {Name="Groceries", Notes="Buy bread, cheese, apples", Done=false},
      new Chore {Name="Devices", Notes="Buy Nexus, Galaxy, Droid", Done=false}
    };

ViewWillAppear 中,将集合传递给源并分配到表视图:

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);

    TableView.Source = new RootTableSource(chores.ToArray());
}

如果现在运行该应用,主屏幕会加载并显示两个任务的列表。 触摸某个任务时,情节提要定义的 segue 将导致显示详细信息屏幕,但此时不会显示任何数据。

若要在 segue 中“发送参数”,请重写 PrepareForSegue 方法并设置 DestinationViewController 的属性(本例中为 TaskDetailViewController)。 目标视图控制器类已实例化,但尚未向用户显示 - 这意味着可以在该类上设置属性,但不能修改任何 UI 控件:

public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
    {
      if (segue.Identifier == "TaskSegue") { // set in Storyboard
        var navctlr = segue.DestinationViewController as TaskDetailViewController;
        if (navctlr != null) {
          var source = TableView.Source as RootTableSource;
          var rowPath = TableView.IndexPathForSelectedRow;
          var item = source.GetItem(rowPath.Row);
          navctlr.SetTask (this, item); // to be defined on the TaskDetailViewController
        }
      }
    }

TaskDetailViewController 中,SetTask 方法将其参数分配到属性,以便可以在 ViewWillAppear 中引用它们。 无法在 SetTask 中修改控件属性,因为调用 PrepareForSegue 时控件属性可能不存在:

Chore currentTask {get;set;}
    public ItemViewController Delegate {get;set;} // will be used to Save, Delete later

public override void ViewWillAppear (bool animated)
    {
      base.ViewWillAppear (animated);
      TitleText.Text = currentTask.Name;
      NotesText.Text = currentTask.Notes;
      DoneSwitch.On = currentTask.Done;
    }

    // this will be called before the view is displayed
    public void SetTask (ItemViewController d, Chore task) {
      Delegate = d;
      currentTask = task;
    }

segue 现在将打开详细信息屏幕并显示选定的任务信息。 遗憾的是,Save 和 Delete 按钮没有实现。 在实现这些按钮之前,请将这些方法添加到 ItemViewController.cs 以更新基础数据并关闭详细信息屏幕

public void SaveTask(Chores chore)
{
  var oldTask = chores.Find(t => t.Id == chore.Id);
        NavigationController.PopViewController(true);
}

public void DeleteTask(Chores chore)
{
  var oldTask = chores.Find(t => t.Id == chore.Id);
  chores.Remove(oldTask);
        NavigationController.PopViewController(true);
}

接下来,需要将按钮的 TouchUpInside 事件处理程序添加到 TaskDetailViewController.cs 的 ViewDidLoad 方法中。 对 ItemViewControllerDelegate 属性引用是专门创建的,因此我们可以调用 SaveTaskDeleteTask,以便在操作过程中关闭此视图:

SaveButton.TouchUpInside += (sender, e) => {
        currentTask.Name = TitleText.Text;
        currentTask.Notes = NotesText.Text;
        currentTask.Done = DoneSwitch.On;
        Delegate.SaveTask(currentTask);
      };

DeleteButton.TouchUpInside += (sender, e) => Delegate.DeleteTask(currentTask);

剩下的最后一项要生成的功能是创建新任务。 在 ItemViewController.cs 中添加一个用于创建新任务并打开详细信息视图的方法。 若要实例化情节提要中的视图,请使用 InstantiateViewController 方法以及该视图的 Identifier - 在此示例中为“detail”:

public void CreateTask () 
    {
      // first, add the task to the underlying data
      var newId = chores[chores.Count - 1].Id + 1;
      var newChore = new Chore{Id = newId};
      chores.Add (newChore);

      // then open the detail view to edit it
      var detail = Storyboard.InstantiateViewController("detail") as TaskDetailViewController;
      detail.SetTask (this, newChore);
      NavigationController.PushViewController (detail, true);
    }

最后,在 ItemViewController.cs 的 ViewDidLoad 方法中连接导航栏中的按钮以调用它:

AddButton.Clicked += (sender, e) => CreateTask ();

情节提要示例的演练到此完成 – 已完成的应用如下所示:

Finished app

该示例演示了以下操作:

  • 创建一个包含原型内容的表,可以重用其中定义的单元格来显示数据列表。
  • 创建一个包含静态内容的表,以生成输入表单。 这包括更改表样式以及添加部分、单元格和 UI 控件。
  • 如何创建 segue 并重写 PrepareForSegue 方法,以通知目标视图所需的任何参数。
  • 使用 Storyboard.InstantiateViewController 方法直接加载情节提要视图。