Table View Programming Guide for iOS 官方文档翻译

About Table Views in iOS Apps( iOS应用程序中的Table View)

Table views are versatile user interface objects frequently found in iOS apps. A table view presents data in a scrollable list of multiple rows that may be divided into sections.

Table view是在iOS应用程序中经常使用的多用途用户界面对象。 表格视图呈现多行可滚动列表中的数据,可将这些数据划分为多个section。

Table views have many purposes:

表格视图有很多目的:

  • To let users navigate through hierarchically structured data
  • To present an indexed list of items
  • To display detail information and controls in visually distinct groupings
  • To present a selectable list of options

  • 让用户浏览分层结构化的数据

  • 显示项目的索引列表
  • 以视觉上不同的分组显示详细信息和控件
  • 提供可选的选项列表

图I-1各种表格视图

A table view has only one column and allows vertical scrolling only. It consists of rows in sections. Each section can have a header and a footer that displays text or an image. However, many table views have only one section with no visible header or footer. Programmatically, the UIKit framework identifies rows and sections through their index number: Sections are numbered 0 through n – 1 from the top of a table view to the bottom; rows are numbered 0 through n – 1 within a section. A table view can have its own header and footer, distinct from any section; the table header appears before the first row of the first section, and the table footer appears after the last row of the last section.

表格视图只有一列,只允许垂直滚动。 它由多个部分组成。 每个部分都可以有一个标题和一个显示文字或图像的页脚。 但是,许多表视图只有一个部分没有可见的页眉或页脚。 以编程方式,UIKit框架通过它们的索引号来标识行和部分:部分从表视图的顶部到底部编号为0到n - 1; 行在一个段内从0到n - 1编号。表格视图可以有自己的页眉和页脚,与任何章节截然不同; 表格标题出现在第一部分的第一行之前,并且表格页脚出现在最后一部分的最后一行之后。

At a Glance (概览)

A table view is an instance of the UITableView class in one of two basic styles, plain or grouped. A plain table view is an unbroken list; a grouped table view has visually distinct sections. A table view has a data source and might have a delegate. The data source object provides the data for populating the sections and rows of the table view. The delegate object customizes its appearance and behavior.

表视图是UITableView类的一个实例,它是两种基本样式之一,plain或grouped。 普通表格视图是一个不间断的列表; 分组表格视图具有视觉上不同的部分。 表格视图有一个数据源并可能有一个代理 。 数据源对象提供用于填充表视图的部分和行的数据。 代理对象定制其外观和行为。

Table Views Draw Their Rows Using Cells (表视图使用单元格绘制其行)

A table view draws its visible rows using cells—that is, UITableViewCell objects. Cells are views that can display text, images, or other kinds of content. They can have background views for both normal and selected states. Cells can also have accessory views, which function as controls for selecting or setting an option.

表视图使用单元格绘制其可见行 - 即UITableViewCell对象。 单元格是可以显示文本,图像或其他类型内容的视图。 他们可以拥有正常和选定状态的背景视图。 cell 还可以有附件视图,用作选择或设置选项的控件。

The UIKit framework defines four standard cell styles, each with its own layout of the three default content elements: main label, detail label, and image. You may also create your own custom cells to acquire a distinctive style for your app’s table views.

UIKit框架定义了四种标准单元格样式,每种样式都具有三种默认内容元素(主标签,详细标签和图像)的布局。 您也可以创建自己的自定义单元格,以获取应用程序的表格视图的独特风格。

When you configure the attributes of a table view in the storyboard editor, you choose between two types of cell content: static cells or dynamic prototypes.

在故事板编辑器中配置表格视图的属性时,可以在两种类型的单元格内容中进行选择:静态单元格或动态原型。

  • Static cells. Use static cells to design a table with a fixed number of rows, each with its own layout. Use static cells when you know what the table looks like at design time, regardless of the specific information it displays.
  • Dynamic prototypes. Use dynamic prototypes to design one cell and then use it as the template for other cells in the table. Use a dynamic prototype when multiple cells in a table should use the same layout to display information. Dynamic prototype content is managed by the data source at runtime, with an arbitrary number of cells.

  • 静态单元格 。 使用静态单元设计具有固定行数的表格,每个表格都有自己的布局。 不管显示的具体信息如何,在设计时知道表的外观时,请使用静态单元格。

  • 动态原型 。 使用动态原型设计一个单元格,然后将其用作表格中其他单元格的模板。 当表中的多个单元格应使用相同的布局来显示信息时,请使用动态原型。 动态原型内容由运行时的数据源进行管理,具有任意数量的单元。

Responding to Selections of Rows (响应选中的行)

When users select a row (by tapping it), the delegate of the table view is informed via a message. The delegate is passed the indexes of the row and the section that the row is in. It uses this information to locate the corresponding item in the app’s data model. This item might be at an intermediate level in the hierarchy of data or it might be a “leaf node” in the hierarchy. If the item is at an intermediate level, the app displays a new table view. If the item is a leaf node, the app displays details about the selected item in a grouped-style table view or some other kind of view.

当用户选择一行(通过点击它)时,表视图的代理通过消息被通知。 该代理会传递该行的索引和该行所在的部分。它使用此信息在应用程序的数据模型中找到相应的项目。 此项可能处于数据层次结构的中间级别,或者它可能是层次结构中的“叶节点”。如果项目处于中间级别,则应用程序将显示新的表格视图。如果项目是叶节点,该应用程序在分组样式的表格视图或某种其他类型的视图中显示有关所选项目的详细信息。

In table views that list a series of options, tapping a row simply selects its associated option. No subsequent view of data is displayed.

在列出一系列选项的表格视图中,点击一行即可选择相关的选项。 没有显示后续的数据视图。

In Editing Mode You Can Add, Delete, and Reorder Rows (在编辑模式下,您可以添加,删除和重新排列行)

Table views can enter an editing mode in which users can insert or delete rows, or relocate them within the table. In editing mode, rows that are marked for insertion or deletion display a green plus sign (insertion) or a red minus sign (deletion) near the left edge of the row. If users touch a deletion control or, in some table views, swipe across a row, a red Delete button appears, prompting users to delete that row. Rows that can be relocated display (near their right edge) an image consisting of several horizontal lines. When the table view leaves editing mode, the insertion, deletion, and reordering controls disappear.

表视图可以进入编辑模式,用户可以在该模式下插入或删除行,或者在表内重新定位行。 在编辑模式下,标记为插入或删除的行在行左边附近显示绿色加号(插入)或红色减号(删除)。 如果用户触摸删除控件,或者在某些表格视图中横跨一行进行滑动,则会出现一个红色的“删除”按钮,提示用户删除该行。 可以重新定位的行(靠近其右边缘)显示由多条水平线组成的图像。 当表格视图离开编辑模式时,插入,删除和重新排序控制消失。

When users attempt to insert, delete, or reorder rows, the table view sends a sequence of messages to its data source and delegate so that they can manage these operations.

当用户尝试插入,删除或重新排序行时,表视图将一系列消息发送到其数据源和代理,以便他们可以管理这些操作。

To Create a Table View, Use a Storyboard(要创建表视图,请使用Storyboard)

The easiest and recommended way to create and manage a table view is to use a custom UITableViewController object in a storyboard. If your app is based largely on table views, create your Xcode project using the Master-Detail Application template. This template includes an initial custom UITableViewController class and a storyboard for the scenes in the user interface, including the custom view controller and its table view. In the storyboard editor, choose one of the two styles for this table view and design its content.

创建和管理表格视图的最简单和推荐的方法是在故事板中使用自定义的UITableViewController对象。 如果您的应用程序主要基于表视图,请使用Master-Detail Application template创建您的Xcode项目。 该模板包含一个初始的自定义UITableViewController类和一个用户界面场景的故事板,包括自定义视图控制器及其表格视图。 在故事板编辑器中,为此表格视图选择两种样式中的一种并设计其内容。

At runtime, UITableViewController creates the table view and assigns itself as delegate and data source. Immediately after it’s created, the table view asks its data source for the number of sections, the number of rows in each section, and the table view cell to use to draw each row. The data source manages the application data used for populating the sections and rows of the table view.

在运行时, UITableViewController创建表视图并将自己指定为代理和数据源。 在创建后,表视图会向其数据源询问段的数量,每段中的行数以及用于绘制每行的表视图单元格。 数据源管理用于填充表视图的部分和行的应用程序数据。

Prerequisites(先决条件)

Before reading this document, you should read Start Developing iOS Apps Today to understand the basic process for developing iOS apps. Then read View Controller Programming Guide for iOS for a comprehensive look at view controllers and storyboards. Finally, to gain valuable hands-on experience using table views in a storyboard, read the tutorial Your Second iOS App: Storyboards.

在阅读本文档之前,您应该阅读 立即开始开发iOS应用程序 了解开发iOS应用程序的基本过程。 然后阅读适用于iOS的View Controller编程指南,以全面了解视图控制器和故事板。最后,为了在故事板中使用表格视图获得宝贵的实践经验,请阅读教程 你的第二个iOS应用程序:故事板 。

The information presented in this introduction and in Table View Styles and Accessory Views summarizes prescriptive information on table views presented in iOS Human Interface Guidelines. You can find a complete description of the styles and characteristics of table views, as well as their recommended uses, in the chapter Content Views.

本介绍和表格视图样式和附件视图中显示的信息总结了表格视图的规定信息 iOS人机界面指南 。 您可以在本章中找到关于表格视图样式和特征的完整说明,以及它们的推荐用途 内容视图 。

See Also(也可以看看)

You will find the following sample code projects to be instructive models for your own table view implementations:
* SimpleDrillDown project
* Table View Animations and Gestures project
For guidance on how to use the standard container view controllers provided by UIKit, see View Controller Catalog for iOS. This document describes split view controllers and navigation controllers, which can both contain table view controllers as children.

您会发现以下示例代码项目是您自己的表视图实现的指导性模型:
* SimpleDrillDown 项目
* 表视图动画和手势项目
有关如何使用UIKit提供的标准容器视图控制器的指导,请参阅适用于iOS的View Controller Catalog 。 本文档介绍了分割视图控制器和导航控制器,它们都可以包含表视图控制器作为子项。

Table View Styles and Accessory Views(表视图样式和附件视图)

Table views come in distinctive styles that are suitable for specific purposes. In addition, the UIKit framework provides standard styles for the cells used to draw the rows of table views. It also gives you standard accessory views (that is, controls) that you can include in cells.

表视图以独特的风格呈现,适合特定用途。 另外,UIKit框架为用于绘制表格视图行的单元格提供标准样式。 它还为您提供标准配件视图(即控件),您可以将其包含在单元格中。

Table View Styles(表视图样式)

There are two major styles of table views: plain and grouped. The two styles are distinguished mainly by appearance.

有两种主要的表格视图样式:plain和grouped。 这两种风格主要通过外观来区分。

Plain Table Views(普通表格视图)

A table view in the plain (or regular) style displays rows that stretch across the screen and have a creamy white background (see Figure 1-1). A plain table view can have one or more sections, sections can have one or more rows, and each section can have its own header or footer title. (A header or footer may also have a custom view, for instance one containing an image). When the user scrolls through a section with many rows, the header of the section floats to the top of the table view and the footer of the section floats to the bottom.

以plain(或常规)样式显示的表格视图可显示横跨屏幕并具有奶白色背景的行(请参阅图1-1 )。 plain表格视图可以有一个或多个section,section可以有一个或多个行,每个section可以有自己的页眉或页脚标题。 (页眉或页脚也可以具有自定义视图,例如包含图像的视图)。 当用户滚动浏览多行的部分时,部分的标题浮动到表视图的顶部,部分的页脚浮动到底部。

Figure 1-1  A table view in the plain style

A variation of plain table views associates an index with sections for quick navigation; Figure 1-2 shows an example of this kind of table view, which is called an indexed list. The index runs down the right edge of the table view. Entries in the index correspond to section header titles. Touching an item in the index scrolls the table view to the associated section. For example, the section headings could be two-letter state abbreviations, and the rows for a section could be the cities in that state; touching at a certain spot in the index displays the cities for the selected state. The rows in indexed lists should not have disclosure indicators or detail disclosure buttons, because these interfere with the index.

plain表格视图的变体将索引与快速导航的部分相关联; 图1-2显示了这种表视图的一个例子,它被称为索引列表 。 索引沿着表格视图的右边缘向下延伸。 索引中的条目对应于节标题标题。 触摸索引中的项目可将表格视图滚动到关联的部分。 例如,section标题可以是双字母状态缩写,而section的行可以是该州的城市; 触摸索引中的某个点显示所选状态的城市。 索引列表中的行不应该有披露指标或详细披露按钮,因为这些行会干扰索引。

Figure 1-2  A table view configured as an indexed list

The simplest kind of table view is a selection list (see Figure 1-3). A selection list is a plain table view that presents a menu of options that users can select. It can limit the selection to one row or allow multiple selections. A selection list marks a selected row with a checkmark (see Figure 1-3).

最简单的表格视图是一个选择列表( 见图1-3 )。 选择列表是一个普通表格视图,显示用户可以选择的选项菜单。 它可以将选择限制为一行或允许多个选择。 选择列表用选中标记选中一行(参见图1-3 )。

Figure 1-3  A table view configured as a selection list

Grouped Table Views

A grouped table view also displays a list of information, but it groups related rows in visually distinct sections. As shown in Figure 1-4, each section has rounded corners and by default appears against a bluish-gray background. Each section may have text or an image for its header or footer to provide some context or summary for the section. A grouped table works especially well for displaying the most detailed information in a data hierarchy. It allows you to separate details into conceptual groups and provide contextual information to help users understand it quickly.

grouped表格视图还显示信息列表,但它在视觉上不同的部分将相关的行分组。 如图1-4所示,每个部分都有圆角,默认情况下会出现蓝灰色的背景。 每个部分可以具有文本或图像作为其页眉或页脚,以为该部分提供一些上下文或摘要。 分组表特别适用于在数据层次结构中显示最详细的信息。 它允许您将细节分解为概念组,并提供上下文信息以帮助用户快速理解。

Figure 1-4  A table view in the grouped style

The headers and footers of sections in a grouped table view have relative locations and sizes as indicated in Figure 1-5.

分组表格视图中部分的页眉和页脚具有相对位置和大小, 如图1-5所示 。

Figure 1-5  Header and footer of a section

On iPad devices, a grouped table view automatically gets wider margins when the table view itself is wide.

在iPad设备上,当表格视图本身很宽时,分组表格视图会自动获得更宽的边距。

Standard Styles for Table View Cells(表格视图单元格的标准样式)

In addition to defining two styles of table views, the UIKit framework defines four styles for the cells that a table view uses to draw its rows. You may create custom table view cells with different appearances if you want, but these four predefined cell styles are suitable for most purposes. The techniques for creating table view cells in a predefined style and for creating custom cells are described in A Closer Look at Table View Cells.

除了定义两种表格视图风格之外,UIKit框架还为表格视图用于绘制行的单元格定义了四种样式。 如果需要,您可以创建具有不同外观的自定义表格视图单元格,但这四种预定义单元格样式适用于大多数目的。 在“查看表格视图单元格”中描述了以预定义样式创建表格视图单元格和创建自定义单元格的技术 。

The default style for table view rows uses a simple cell style that has a single title and an optional image (Figure 1-6). This style is associated with the UITableViewCellStyleDefault constant.

表格视图行的默认样式使用简单的单元格样式,该样式具有单个标题和可选图像( 图1-6 )。 此样式与UITableViewCellStyleDefault常量关联。

Figure 1-6  Default table row style

The cell style for the rows in Figure 1-7 left-aligns the main title and puts a gray subtitle under it. It also permits an image in the default image location. This style is associated with the UITableViewCellStyleSubtitle constant.

图1-7中行的单元格样式左对齐主标题,并在其下放置灰色字幕。 它还允许默认图像位置中的图像。 这种风格与UITableViewCellStyleSubtitle常量相关联。

Figure 1-7  Table row style with a subtitle under the title

The cell style for the rows in Figure 1-8 left-aligns the main title. It puts the subtitle in blue text and right-aligns it on the right side of the row. Images are not permitted. This style is used in the Settings app, where the subtitle indicates the current setting for a preference. It is associated with the UITableViewCellStyleValue1 constant.

图1-8中的行的单元格样式左对齐主标题。 它将字幕放在蓝色文本中,并将其右对齐到该行的右侧。 图像不被允许。 此样式用于“设置”应用中,其中字幕指示首选项的当前设置。 它与UITableViewCellStyleValue1常量相关联。

Figure 1-8  Table row style with a right-aligned subtitle

The cell style for the rows in Figure 1-9 puts the main title in blue and right-aligns it at a point that’s indented from the left side of the row. The subtitle is left aligned at a short distance to the right of this point. This style does not allow images. It is used in the Contacts part of the Phone app and is associated with the UITableViewCellStyleValue2 constant.

图1-9中的行的单元格样式将主标题设置为蓝色,并将其右对齐,该行从左侧开始缩进。 小标题左对齐在这一点的右边一小段距离。 这种风格不允许图像。 它在Phone应用程序的Contacts部分中使用,并与UITableViewCellStyleValue2常量关联。

Figure 1-9  Table row style in Contacts format

Accessory Views(配件视图)

There are three standard kinds of accessory views (shown with their accessory-type constants):

有三种标准配件视图(用附件类型常量显示):

Disclosure indicator—UITableViewCellAccessoryDisclosureIndicator. You use the disclosure indicator when selecting a cell results in the display of another table view reflecting the next level in the data model hierarchy.

发现指示器 - UITableViewCellAccessoryDisclosureIndicator 。 在选择单元格时,使用发现指示器会显示反映数据模型层次结构中下一个级别的其他表视图。

Detail disclosure button—UITableViewCellAccessoryDetailDisclosureButton. You use the detail disclosure button when selecting a cell results in a detail view of that item (which may or may not be a table view).

详细信息显示按钮 - UITableViewCellAccessoryDetailDisclosureButton 。 在选择单元格时,您可以使用详细信息显示按钮导出该项目的详细视图(可能是或不是表格视图)。

Checkmark—UITableViewCellAccessoryCheckmark. You use a checkmark when a touch on a row results in the selection of that item. This kind of table view is known as a selection list, and it is analogous to a pop-up list. Selection lists can limit selections to one row, or they can allow multiple rows with checkmarks.

选中标记 - UITableViewCellAccessoryCheckmark 。 当触摸某一行会导致选择该项目时,您会使用复选标记。 这种表格视图被称为选择列表,类似于弹出列表。 选择列表可以将选择限制为一行,或者可以允许具有复选标记的多行。

Instead of the standard accessory views, you may specify a control (for example, a switch) or a custom view as the accessory view.

作为附件视图,您可以指定控件(例如开关)或自定义视图,而不是标准附件视图。

Overview of the Table View API(Table View API概述)

The table view programming interface includes several UIKit classes, two formal protocols, and a category added to a Foundation framework class.

表视图编程接口包括几个UIKit类,两个正式的 协议 ,和一个类别 添加到基础框架类。

Table View

A table view itself is an instance of the UITableView class. You use its methods to configure the appearance of the table view—for example, specifying the default height of rows or providing a subview used as the header for the table. Other methods give you access to the currently selected row as well as specific rows or cells. You can call other methods of UITableView to manage selections, scroll the table view, and insert or delete rows and sections.

表视图本身是UITableView类的一个实例。 您可以使用它的方法来配置表视图的外观,例如,指定行的缺省高度或提供用作表的标题的子视图。 其他方法可让您访问当前选定的行以及特定的行或单元格。 您可以调用UITableView其他方法来管理选择,滚动表视图以及插入或删除行和部分。

UITableView inherits from the UIScrollView class, which defines scrolling behavior for views with content larger than the size of the window. UITableView redefines the scrolling behavior to allow vertical scrolling only.

UITableView继承自UIScrollView类,该类定义了内容大于窗口大小的视图的滚动行为。 UITableView重新定义了滚动行为,只允许垂直滚动。

Table View Controller

The UITableViewController class manages a table view and adds support for many standard table-related behaviors such as selection management, row editing, table configuration, and others. This additional support is there to minimize the amount of code you have to write to create and initialize your table-based interface. You don’t use this class directly—instead you subclass UITableViewController to add custom behaviors.

UITableViewController类管理一个表视图,并添加对许多标准表相关行为的支持,如选择管理,行编辑,表格配置等。 这种额外的支持是为了最大限度地减少创建和初始化基于表格的界面所需编写的代码量。 你不直接使用这个类 - 而是你的子类UITableViewController添加自定义行为。

Data Source and Delegate

A UITableView object must have a delegate and a data source. Following the Model-View-Controller design pattern, the data source mediates between the app’s data model (that is, its model objects) and the table view. The delegate, on the other hand, manages the appearance and behavior of the table view. The data source and the delegate are often (but not necessarily) the same object, and that object is usually a custom subclass of UITableViewController. (See Navigating a Data Hierarchy with Table Views for further information.)

一个UITableView对象必须有一个 代表和数据源 。 之后 模型-视图-控制器 设计模式中,数据源在应用程序的数据模型(即其数据模型)之间作为 模型对象 和表格视图的中介。另一方面,代理管理表视图的外观和行为。 数据源和代理通常(但不一定)是同一个对象,并且该对象通常是UITableViewController的自定义子类。 (请参阅使用表视图导航数据层次结构以获取更多信息。)

The data source adopts the UITableViewDataSource protocol. UITableViewDataSource has two required methods. The tableView:numberOfRowsInSection: method tells the table view how many rows to display in each section, and the tableView:cellForRowAtIndexPath: method provides the cell to display for each row in the table. Optional methods allow the data source to configure multiple sections, provide headers and/or footers, and support adding, removing, and reordering rows in the table.

数据源采用UITableViewDataSource协议。 UITableViewDataSource有两个必需的方法。 tableView:numberOfRowsInSection:方法告诉表视图在每个部分中显示多少行, tableView:cellForRowAtIndexPath:方法提供要显示表中每一行的单元格。 可选方法允许数据源配置多个section,提供页眉和/或页脚,并支持添加,删除和重新排序表中的行。

The delegate adopts the UITableViewDelegate protocol. This protocol has no required methods. It declares methods that allow the delegate to modify visible aspects of the table view, manage selections, support an accessory view, and support editing of individual rows in a table.

该代理采用UITableViewDelegate协议。 该协议没有必要的方法。 它声明允许委托修改表视图的可见方面,管理选择,支持附件视图以及支持编辑表中单个行的方法。

An app can make use of the convenience class UILocalizedIndexedCollation to help the data source organize the data for indexed lists and display the proper section when users tap an item in the index. The UILocalizedIndexedCollation class also localizes section titles.

应用程序可以使用便利的UILocalizedIndexedCollation类来帮助数据源组织索引列表的数据,并在用户点击索引中的项目时显示正确的部分。UILocalizedIndexedCollation类还可以定位节标题。

Extension to the NSIndexPath Class(对NSIndexPath类的扩展)

Many table view methods use index paths as parameters or return values. An index path identifies a path to a specific node in a tree of nested arrays, and in the Foundation framework it is represented by an NSIndexPath object. UIKit declares a category on NSIndexPath with methods that return key paths, locate rows in sections, and construct NSIndexPath objects from row and section indexes. For more information, see NSIndexPath UIKit Additions.

许多表视图方法使用索引路径作为参数或返回值。 索引路径标识嵌套数组树中特定节点的路径,而在Foundation框架中,它由NSIndexPath对象表示。 UIKit声明了一个 类别 在NSIndexPath使用返回键路径的方法,在部分中定位行,并根据行和部分索引构造NSIndexPath对象。 有关更多信息,请参阅 NSIndexPath UIKit添加 。

Table View Cells

As noted in Data Source and Delegate, the data source must return a cell object for each visible row that a table view displays. These cell objects must inherit from the UITableViewCell class. This class includes methods for managing cell selection and editing, managing accessory views, and configuring the cell. You can instantiate cells directly in the standard styles defined by the UITableViewCell class and give these cells content consisting of one or two strings of text and, in some styles, both image and text. Instead of using a cell in a standard style, you can put your own custom subviews in the content view of an “off-the-shelf” cell object. You may also subclass UITableViewCell to customize the appearance and behavior of table view cells. These approaches are all discussed in A Closer Look at Table View Cells.

如数据源和代理中所述,数据源必须为表视图显示的每个可见行返回一个单元对象。 这些单元对象必须从UITableViewCell类继承。 该课程包括管理单元格选择和编辑,管理配件视图以及配置单元格的方法。 您可以 实例 单元格直接处于由UITableViewCell类定义的标准样式中,并为这些单元格提供由一个或两个文本字符串组成的内容,并且在某些样式中包含图像和文本。 您可以将自己的自定义子视图放置在“现成”单元对象的内容视图中,而不是使用标准样式的单元。 您也可以继承UITableViewCell以定制表格视图单元格的外观和行为。 这些方法都在“仔细查看表格视图单元格”中讨论 。

A common use of table views—and one to which they’re ideally suited—is to navigate hierarchies of data. A table view at a top level of the hierarchy lists categories of data at the most general level. Users select a row to “drill down” to the next level in the hierarchy. At the bottom of the hierarchy is a view (often a table view) that presents details about a specific item (for example, an address book record) and may allow users to edit the item. This section explains how you can map the levels of the data model hierarchy to a succession of table views and describes how you can use the facilities of the UIKit framework to help you implement such navigation-based apps.

表视图的一种常见用法 - 以及它们最适合的一种 - 是浏览数据层次结构。 层级顶层的表格视图以最一般的级别列出数据类别。 用户选择一行以“向下钻取”到层次结构中的下一个级别。 层次结构的底部是一个视图(通常是一个表视图),用于呈现特定项目的详细信息(例如,地址簿记录),并允许用户编辑该项目。 本节介绍如何将数据模型层次结构的级别映射到一系列表视图,并介绍如何使用UIKit框架的功能来帮助您实现这种基于导航的应用程序。

Hierarchical Data Models and Table Views(分层数据模型和表视图)

For a navigation-based app, you typically design your app data as a graph of model objects that is sometimes referred to as the app’s data model. You can then implement the model layer of your app using various mechanisms or technologies, including Core Data, property lists, or archives of custom objects. Regardless of the approach, the traversal of your app’s data model follows patterns that are common to all navigation-based apps. The data model has hierarchical depth, and objects at various levels of this hierarchy should be the source for populating the rows of a table view.

对于基于导航的应用程序,您通常将您的应用程序数据设计为图表 模型对象 这有时被称为应用程序的数据模型 。 然后,您可以使用各种机制或技术来实现应用程序的模型层,这些机制或技术包括Core Data, property lists , archives 或者自定义对象。 无论如何,遍历应用程序的数据模型都遵循所有基于导航的应用程序通用的模式。 数据模型具有分层深度,并且此层次结构的各个级别的对象应该是填充表视图的行的源。

Note: To learn about the Core Data technology and framework, see Core Data Starting Point.
注意:要了解核心数据技术和框架,请参阅 核心数据起点 。

The Data Model as a Hierarchy of Model Objects (数据模型作为模型对象的层次结构)

A well-designed app factors its classes and objects in a way that conforms to the Model-View-Controller (MVC) design pattern. The app’s data model consists of the model objects in this pattern. You can describe model objects (using the terminology provided by the object modeling pattern) in terms of their properties. These properties are of two general kinds: attributes and relationships.

一个设计良好的应用程序将其类和对象按照符合的方式进行分解 模型-视图-控制器 (MVC)设计模式。 应用程序的数据模型由此模式中的模型对象组成。 您可以描述模型对象(使用由 对象建模 模式提供的术语))就其属性而言。 这些属性有两种一般类型:属性和关系。

Note: The notion of “property” here is abstractly related to, but not identical with, the declared property feature of Objective-C. A class definition typically represents properties programmatically through instance variables and declared properties.

注意:这里的“property”的概念是比较抽象的相关,但与Objective-C的特性声明属性不相同 。 一个 类定义 通常通过实例变量和声明属性以编程方式表示属性。

Attributes represent elements of model-object data. Attributes can range from an instance of a primitive class (for example, an NSString, NSDate, or UIColor object) to a C structure or a simple scalar value. Attributes are generally what you use to populate a table view that represents a “leaf node” of the data hierarchy and that presents a detail view of that item.

属性表示模型对象数据的元素。 属性的范围可以从 原始类的实例 (例如, NSString , NSDate或UIColor对象)转换为C结构或简单标量值。 属性通常用于填充表示数据层次结构的“叶节点”的表视图,并提供该项目的详细视图。

A model object may also have relationships with other model objects. It is through these relationships that a data model acquires hierarchical depth by composing an object graph. Relationships are of two general kinds in terms of cardinality: to-one and to-many. To-one relationships define an object’s relationship with another object (for example, a parent relationship). A to-many relationship, on the other hand, defines an object’s relationship with multiple objects of the same kind. The to-many relationship is characterized by containment and can be programmatically represented by collections such as NSArray objects (or, simply, arrays). An array might contain other arrays, or it could contain multiple dictionaries, which are collections that identify their contained values through keys. Dictionaries, in turn, can contain one or more other collections, including arrays, sets, and even other dictionaries. As collections nest in other collections, your data model can acquire hierarchical depth.

模型对象也可能与其他模型对象有关系。 通过这些关系,数据模型通过组成对象图获取层级深度。 在基数方面,关系有两种一般类型:一对一和多对多。 一对一关系定义一个对象与另一个对象的关系(例如,父亲关系)。 另一方面,一对多关系定义了一个对象与多个同类对象的关系。 多对多关系的特点是遏制,并可以用编程方式表示 集合 如NSArray对象(或简单地说,数组)。 一个数组可能包含其他数组,或者可能包含多个字典,这些字典是通过键标识其包含的值的集合。 字典反过来可以包含一个或多个其他集合,包括数组,集合甚至其他字典。 由于集合嵌套在其他集合中,因此您的数据模型可以获取分层深度。

Table Views and the Data Model(表视图和数据模型)

The rows of a plain table view are typically backed by collection objects of the app’s data model; these objects are usually arrays. Arrays contain strings or other elements that a table view can use when displaying row content. When you create a table view (described in Creating and Configuring a Table View), it immediately queries its data source for its dimensions—that is, it requests the number of sections and the number of rows per section—and then asks for the content of each row. The data source fetches this content from an array in the appropriate level of the data-model hierarchy.

普通表视图的行通常由后台支持 ,收集对象 的应用程序数据模型; 这些对象通常是数组。 数组包含字符串或表视图在显示行内容时可以使用的其他元素。 当你创建一个表格视图(在创建和配置表格视图中描述)时,它立即查询它的表格 数据源 因为它的尺寸 - 也就是说,它要求section的个数,以及section中的行数 - 然后要求每行的内容。 数据源从数据模型层次结构的适当级别的数组中获取此内容。

In many of the methods defined for a table view’s data source and delegate, the table view passes in an index path to identify the section and row that is the focus of the current operation—for example, fetching content for a row or indicating the row the user tapped. An index path is an instance of the Foundation framework’s NSIndexPath class that you can use to identify an item in a tree of nested arrays. The UIKit framework extends NSIndexPath to add a section and a row property to the class. The data source should use these properties to map a section and row of the table view to a value at the corresponding index of the array being used as the table view’s source of data.

在许多为表格视图的数据源和代理定义的方法中 ,表视图在索引路径中传递以标识作为当前操作焦点的section和row,例如,获取行的内容或指示用户点击的行。 索引路径是Foundation框架的NSIndexPath类的一个实例,可用于标识嵌套数组树中的项目。 UIKit框架扩展NSIndexPath以添加一个section和row 属性 给一个类。 数据源应该使用这些属性将表视图的section和row映射到用作表视图的数据源的数组的相应索引处的值。

In the sequence of table views in Figure 3-1, the top level of the data hierarchy is an array of four arrays, with each inner array containing objects representing the trails for a particular region. When the user selects one of these regions, the next table view lists names identifying the trails within the selected array. When the user selects a particular trail, the next table view describes that trail using a grouped table view.

在图3-1中的表格视图序列中,数据层次结构的顶层是一个由四个数组组成的数组,每个数组的内部数组包含表示特定区域的轨迹的对象。 当用户选择这些区域中的一个时,下一个表格视图将列出标识所选阵列中的路径的名称。 当用户选择特定的路径时,下一个表格视图使用分组表格视图来描述该路径。

Figure 3-1  Mapping levels of the data model to table views

Note: You could easily redesign the app in Figure 3-1 to have only two table views. The first table view would be an indexed list of trails by region. The second table view would display the detail for a selected trail.
注意:您可以轻松地重新设计图3-1中的应用程序,使其只有两个表格视图。 第一个表格视图将是按地区列出的索引列表。 第二个表格视图将显示选定线索的详细信息。

View Controllers and Navigation-Based Apps

The UIKit framework provides a number of view controller classes for managing common user interface patterns in iOS. View controllers are controller objects that inherit from the UIViewController class. They are an essential tool for view management, especially when an app uses those views to present successive levels of its data hierarchy. This section describes how two subclasses of UIViewController, navigation controllers and table view controllers, present and manage a succession of table views.

UIKit框架提供了许多视图控制器类来管理iOS中的通用用户界面模式。 视图控制器是 控制器对象 从UIViewController类继承。 它们是视图管理的重要工具,尤其是当应用程序使用这些视图呈现其数据层次结构的连续级别时。 本节介绍UIViewController ,导航控制器和表视图控制器的两个子类如何呈现和管理一系列表视图。

Note: This section gives an overview of view controllers to provide some background for the coding tasks discussed later in this document. To learn about view controllers in depth, see View Controller Programming Guide for iOS.

注意:本节概述视图控制器,为本文稍后讨论的编码任务提供一些背景知识。 要深入了解视图控制器,请参阅适用于iOS的视图控制器编程指南 。

The UINavigationController class inherits from UIViewController, a base class that defines the common programmatic interface and behavior for controller objects that manage views in iOS. Through inheritance from this base class, a view controller acquires an interface for general view management. After it implements parts of this interface, a view controller can autorotate its view, respond to low-memory notifications, overlay “modal” views, respond to taps on the Edit button, and otherwise manage the view.

UINavigationController类继承自UIViewController , UIViewController是一个基类,它定义了公共编程接口和行为,为 控制器对象 在iOS中管理视图。 通过继承此基类,视图控制器获取一般视图管理的接口。 在它实现了该接口的一部分后,视图控制器可以自动控制其视图,响应低内存通知,覆盖“模态”视图,响应“编辑”按钮上的轻击,并以其他方式管理视图。

A navigation controller maintains a stack of view controllers, one for each of the table views displayed (see Figure 3-2). It begins with what’s known as the root view controller. When the user taps a row of the table view (often on a detail disclosure button), the root view controller pushes the next view controller onto the stack. The new view controller’s table view visually slides into place from the right, and the navigation bar items are updated appropriately. When users tap the back button in the navigation bar, the current view controller is popped off the stack. As a consequence, the navigation controller displays the table view managed by the view controller that is now at the top of the stack.

导航控制器维护视图控制器的一个栈,每个显示的表视图都有一个控制器( 见图3-2 )。 它以所谓的根视图控制器开始 。 当用户点击表格视图的一行时(通常在详细发现按钮上),根视图控制器将下一个视图控制器推入栈。 新的视图控制器的表格视图从右侧以可视方式滑入,并且导航栏项目被适当更新。 当用户点击导航栏中的后退按钮时,当前视图控制器将弹出堆栈。 因此,导航控制器将显示现在位于栈顶部的视图控制器管理的表视图。

Figure 3-2  Navigation controller and view controllers in a navigation-based app

Navigation bars are a user-interface device that enables users to navigate a hierarchy of data. Users start with general, top-level items and “drill down” the hierarchy to detailed views showing specific properties of leaf-node items. The view below the navigation bar presents the current level of data. A navigation bar includes a title for the current view and, if that view is lower in the hierarchy than the top level, a back button on the left side of the bar; the back button is a navigation control that the user taps to return to the previous level. (The back button by default displays the title for the previous view.) A navigation bar may also have an Edit button—used to enter editing mode for the current view—or custom buttons for functions that manage content (see Figure 3-3).

导航栏是一个用户界面设备,使用户能够导航数据层次结构。 用户从一般的顶级项目开始,并将层次结构“向下钻取”到详细的视图,其中显示了叶节点项目的特定属性。 导航栏下方的视图显示当前的数据级别。 导航栏包含当前视图的标题,如果该视图在层次结构中低于顶层,则在该栏左侧有一个后退按钮; 后退按钮是用户点击返回到前一级的导航控件。 (后退按钮默认显示前一个视图的标题。)导航栏还可能有一个编辑按钮 - 用于进入当前视图的编辑模式 - 或管理内容的功能的自定义按钮(请参见图3-3 ) 。

Figure 3-3  Navigation bars and common control items

A UINavigationController manages the navigation bar, including the items that are displayed in the bar for the view below it. A UIViewController object manages a view displayed below the navigation bar. For this view controller, you create a subclass of UIViewController or a subclass of a view controller class that the UIKit framework provides for managing a particular type of view. For table views, this view controller class is UITableViewController. For a navigation controller that displays a sequence of table views reflecting levels within a data hierarchy, you need to create a separate custom table view controller for each table view.

一个UINavigationController管理导航栏,包括在它下面的视图栏中显示的项目。 UIViewController对象管理导航栏下显示的视图。 对于这个视图控制器,您可以创建UIViewController的子类或UIKit框架为管理特定类型的视图提供的视图控制器类的子类。 对于表视图,这个视图控制器类是UITableViewController 。 对于显示反映数据层次内的级别的表视图序列的导航控制器,您需要为每个表视图创建一个单独的定制表视图控制器。

The UIViewController class includes methods that let view controllers access and set the navigation items displayed in the navigation bar for the currently displayed table view. This class also declares a title property through which you can set the title of the navigation bar for the current table view.

UIViewController类包含一些方法,可让视图控制器访问和设置当前显示的表视图的导航栏中显示的导航项目。 这个类也声明了一个title 属性, 通过它你可以为当前表格视图设置导航栏的标题。

Table View Controllers

Although you could manage a table view using a direct subclass of UIViewController, you save yourself a lot of work if instead you subclass UITableViewController. The UITableViewController class takes care of many of the details you would have to implement if you created a direct subclass of UIViewController to manage a table view.

尽管您可以使用UIViewController的直接子类来管理表视图,但如果您是UITableViewController子类,则可以节省大量工作。 如果您创建了UIViewController的直接子类来管理表视图,则UITableViewController类负责处理许多必须实现的细节。

The recommended way to create a table view controller is to specify it in a storyboard. The associated table view is loaded from the storyboard, along with the table view’s attributes, size, and autoresizing characteristics. The table view controller sets itself as the data source and the delegate of the table view.

推荐的创建表格视图控制器的方法是在故事板中指定它。 关联的表格视图是从故事板加载的,以及表格视图的属性,大小和自动调整特性。 表视图控制器将自己设置为数据源和表视图的委托。

Note: You can create a table view controller programmatically by allocating memory for it and initializing it with the initWithStyle: method, passing in either UITableViewStylePlain or UITableViewStyleGrouped for the required table view style.

注意:您可以通过编程方式创建表格视图控制器,分配内存和使用initWithStyle:方法初始化,为所需的表视图样式传递UITableViewStylePlain或UITableViewStyleGrouped 。

When the table view is about to appear for the first time, the table view controller sends reloadData to the table view, which prompts it to request data from its data source. The data source tells the table view how many sections and rows per section it wants, and then gives the table view the data to display in each row. This process is described in Creating and Configuring a Table View.

当表视图即将首次出现时,表视图控制器将reloadData发送到表视图,该视图会提示它从其数据源请求数据。 数据源告诉表视图每个部分有多少section和row,然后给出表格视图显示在每行中的数据。 创建和配置表视图中介绍了此过程。

The UITableViewController class also performs other common tasks. It clears selections when the table view is about to be displayed and flashes the scroll indicators when the table finishes displaying. In addition, it responds properly when users tap the Edit button by putting the table view into editing mode (or taking it out of editing mode if users tap Done). The class exposes one property, tableView, which gives you access to the managed table view.

UITableViewController类还执行其他常见任务。 当表格视图即将显示时,它将清除选择,并在表格完成显示时闪烁滚动指示器。 另外,当用户点击编辑按钮并将表格视图置于编辑模式时(或者如果用户点击完成,则将其从编辑模式中取出),它可以正确响应。 该类公开一个属性tableView ,它可以让您访问托管表视图。

Note: A table view controller supports inline editing of table view rows; if, for example, rows have embedded text fields in editing mode, it scrolls the row being edited above the virtual keyboard that is displayed. It also supports the NSFetchedResultsController class for managing the results returned from a Core Data fetch request.

注意:表格视图控制器支持表格视图行的内联编辑; 例如,如果行在编辑模式下嵌入了文本字段,则它会将正在编辑的行滚动到所显示的虚拟键盘上方。 它还支持NSFetchedResultsController类来管理从核心数据获取请求返回的结果。

The UITableViewController class implements the foregoing behavior by overriding loadView, viewWillAppear:, and other methods inherited from UIViewController. In your subclass of UITableViewController, you may also override these methods to acquire specialized behavior. If you do override these methods, be sure to invoke the superclass implementation of the method, usually as the first method call, to get the default behavior.

UITableViewController类通过覆盖loadView , viewWillAppear:以及从UIViewController继承的其他方法来实现上述行为。 在你的UITableViewController的子类中,你也可以重写这些方法来获得专门的行为。 如果你重写了这些方法,一定要调用该方法的超类实现,通常作为第一个方法调用来获取默认行为。

Note: You should use a UIViewController subclass rather than a subclass of UITableViewController to manage a table view if the view to be managed is composed of multiple subviews, only one of which is a table view. The default behavior of the UITableViewController class is to make the table view fill the screen between the navigation bar and the tab bar (if either are present).
If you decide to use a UIViewController subclass rather than a subclass of UITableViewController to manage a table view, you should perform a couple of the tasks mentioned above to conform to the human interface guidelines. To clear any selection in the table view before it’s displayed, implement the viewWillAppear: method to clear the selected row (if any) by calling deselectRowAtIndexPath:animated:. After the table view has been displayed, you should flash the scroll view’s scroll indicators by sending a flashScrollIndicators message to the table view; you can do this in an override of the viewDidAppear: method of UIViewController.

注意:如果要管理的视图由多个子视图组成,并且只有其中一个是表视图,则应该使用UIViewController子类而不是UITableViewController的子类来管理表视图。UITableViewController类的默认行为是使表视图填充导航栏和选项卡栏(如果存在)之间的屏幕。

如果您决定使用UIViewController子类而不是UITableViewController的子类来管理表视图,则应执行上述几项任务以符合人机界面指南。 要在显示表视图之前清除所有选择,请通过调用deselectRowAtIndexPath:animated:来实现viewWillAppear:方法以清除选定行(如果有)。 在表格视图显示之后,您应该通过向表格视图发送flashScrollIndicators消息来刷新滚动视图的滚动指示器; 你可以通过覆盖UIViewController的viewDidAppear:方法来做到这一点。

Managing Table Views in a Navigation-Based App(管理基于导航的应用程序中的表格视图)

A UITableViewController object—or any other object that assumes the roles of data source and delegate for a table view—must respond to messages sent by the table view in order to populate its rows, configure it, respond to selections, and manage editing sessions. In the rest of this document, you learn how to do these things. However, there are certain other things you need to do to ensure the proper display of a sequence of table views in a navigation-based app.

UITableViewController对象 - 或承担角色的任何其他对象,对于表视图的数据源和代理,必须对表视图发送的消息进行响应,以便填充行,配置它,响应选择和管理编辑会话。 在本文档的其余部分,您将学习如何执行这些操作。 但是,还需要执行某些其他操作以确保在基于导航的应用程序中正确显示一系列表视图。

Note: This section summarizes view-controller and navigation-controller tasks, with a focus on table views. For a thorough discussion of view controllers and navigation controllers, including the complete details of their implementation, see View Controller Programming Guide for iOS and View Controller Catalog for iOS.

注意:本部分总结了视图控制器和导航控制器任务,重点讨论表视图。 有关视图控制器和导航控制器的全面讨论,包括其实现的完整细节,请参阅适用于iOS的View Controller编程指南和适用于iOS的 View Controller Catalog

At this point, let’s assume that a table view managed by a table view controller presents a list to the user. How does the app display the next table view in the sequence?

此时,我们假设由表视图控制器管理的表视图为用户提供了一个列表。 应用程序如何在序列中显示下一个表格视图?

When a user taps a row of the table view, the table view calls the tableView:didSelectRowAtIndexPath: or tableView:accessoryButtonTappedForRowWithIndexPath: method implemented by the delegate. (That latter method is invoked if the user taps a row’s detail disclosure button.) The delegate creates the table view controller managing the next table view in the sequence, sets the data it needs to populate its table view, and pushes this new view controller onto the navigation controller’s stack of view controllers. A storyboard provides the specification that allows UIKit to perform most of this work for you.

当用户点击表视图的一行时,表视图调用委托实现的tableView:didSelectRowAtIndexPath:或tableView:accessoryButtonTappedForRowWithIndexPath:方法。(如果用户点击行的详细披露按钮,则调用后一个方法。)委托创建表视图控制器,管理序列中的下一个表视图,设置填充其表视图所需的数据,并将此新视图控制器到导航控制器的视图控制器栈上。 故事板提供了允许UIKit为您执行大部分此项工作的规范。

Storyboards represent the screens in an app and the transitions between them. The storyboard in a basic app may contain just a few screens, but a more complex app might have multiple storyboards, each of which represents a different subset of its screens. The storyboard example in Figure 3-4 presents a graphical representation of each scene, its contents, and its connections.

故事板代表应用程序中的屏幕以及它们之间的转换。 基本应用程序中的故事板可能只包含几个屏幕,但更复杂的应用程序可能包含多个故事板,每个故事板都代表其屏幕的不同子集。图3-4中的故事板示例展示了每个场景及其内容和连接的图形表示。

Figure 3-4  A storyboard with two table view controllers

A scene represents an onscreen content area that is managed by a view controller. (In the context of a storyboard, scene and view controller are synonymous terms.) The leftmost scene in the default storyboard represents a navigation controller. A navigation controller is a container view controller because, in addition to its views, it also manages a set of other view controllers. For example, the navigation controller in Figure 3-4 manages the master and detail view controllers, in addition to the navigation bar and the back button that you see when you run the app.

scene表示由视图控制器管理的屏幕上内容区域。 (在故事板中,scene和视图控制器是同义词。)默认故事板中最左边的scene表示导航控制器。 导航控制器是一个容器视图控制器,因为除了视图之外,它还管理一组其他视图控制器。 例如, 图3-4中的导航控制器除了在运行应用程序时看到的导航栏和后退按钮之外,还管理主视图控制器和详细视图控制器。

A relationship is a type of connection between scenes. In Figure 3-4, there is a relationship between the navigation controller and the master scene. In this case, the relationship represents the containment of the master and detail scenes by the navigation controller. When the app runs, the navigation controller automatically loads the master scene and displays the navigation bar at the top of the screen.

关系是场景之间的一种连接。 在图3-4中 ,导航控制器和主场景之间存在关系。 在这种情况下,该关系表示导航控制器包含主场景和细节场景。 当应用程序运行时,导航控制器会自动加载主场景,并在屏幕顶部显示导航栏。

A segue represents a transition from one scene (called the source) to the next scene (called the destination). For example, in Figure 3-4, the master scene is the source and the detail scene is the destination. When you select the Detail item in the master list, you trigger a segue from the source to the destination. In this case, the segue is a push segue, which means that the destination scene slides over the source scene from right to left. As the detail screen is revealed, a back button appears at the left end of the navigation bar, titled with the previous screen’s title (in this case, “Master”). The back button is provided automatically by the navigation controller that manages the master-detail hierarchy.

segue表示从一个场景(称为源)到下一个场景(称为目的地)的过渡。 例如,在图3-4中 ,主场景是源,细节场景是目的地。 当您在主列表中选择明细项目时,您会触发从源到目的地的分段。 在这种情况下,segue是push segue,这意味着目标场景从右向左滑过源场景。 当详细信息屏幕显示时,导航栏左端将显示一个后退按钮,标题为前一屏幕的标题(在本例中为“主”)。 后退按钮由管理主 - 细节层次结构的导航控制器自动提供。

Storyboards make it easy to pass data from one scene to another via the prepareForSegue:sender: method of the UIViewController class. This method is called when the first scene (the source) is about to transition to the next scene (the destination). The source view controller can implement prepareForSegue:sender: to perform setup tasks, such as passing information to the destination view controller about what it should display in its table view. Listing 3-1 shows one implementation of this method.

通过故事板可以轻松地通过UIViewController类的prepareForSegue:sender:方法将数据从一个场景传递到另一个场景。 当第一个场景(源)即将转换到下一个场景(目标)时调用此方法。 源视图控制器可以实现prepareForSegue:sender:执行设置任务,例如将信息传递给目标视图控制器,以了解它应该在其表视图中显示的内容。 清单3-1显示了此方法的一个实现。

Listing 3-1 Passing data to a destination view controller

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
  if ([[segue identifier] isEqualToString:@"ShowDetails"]) {
    MyDetailViewController *detailViewController = [segue destinationViewController];
    NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    detailViewController.data = [self.dataController objectInListAtIndex:indexPath.row];
  }
}

A segue represents a one-way transition from a source scene to a destination scene. One of the consequences of this design is that you can use a segue to pass data to a destination, but you can’t use a segue to send data from a destination to its source. To solve this problem, you create a delegate protocol that declares methods that the destination view controller calls when it needs to pass back some data.

segue表示从源场景到目标场景的单向过渡。 这种设计的一个后果是,您可以使用segue将数据传递到目标,但不能使用segue将数据从目标发送到源。 为了解决这个问题,你需要创建一个代理协议来声明当目标视图控制器需要传回一些数据时调用的方法。

Listing 3-2 shows one implementation of a protocol for passing data back to a source view controller.

清单3-2显示了将数据传回给源视图控制器的协议的一个实现。

Listing 3-2 Passing data to a source view controller

@protocol MyAddViewControllerDelegate <NSObject>
- (void)addViewControllerDidCancel:(MyAddViewController *)controller;
- (void)addViewControllerDidFinish:(MyAddViewController *)controller data:(NSString *)item;
@end

- (void)addViewControllerDidCancel:(MyAddViewController *)controller {
  [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)addViewControllerDidFinish:(MyAddViewController *)controller data:(NSString *)item {
  if ([item length]) {
    [self.dataController addData:item];
    [[self tableView] reloadData];
  }
  [self dismissViewControllerAnimated:YES completion:NULL];
}

Note: The full details of creating storyboards are described in Xcode User Guide. To learn more about using view controllers in storyboards, see View Controller Programming Guide for iOS.

注意:有关创建故事板的完整详细信息, 请参阅 Xcode用户指南 。 要了解更多关于在故事板中使用视图控制器的信息,请参阅适用于iOS的View Controller编程指南

Design Pattern for Navigation-Based Apps(基于导航的应用程序的设计模式)

A navigation-based app with table views should follow these design best practices:

具有表格视图的基于导航的应用程序应遵循以下设计最佳做法:

  • A view controller (typically a subclass of UITableViewController), acting in the role of data source, populates its table view with data from an object representing a level of the data hierarchy.
    When the table view displays a list of items, the object is typically an array. When the table view displays item detail (that is, a leaf node of the data hierarchy), the object can be a custom model object, a Core Data managed object, a dictionary, or something similar.
  • The view controller stores the data it needs for populating its table view.
    The view controller can use this data directly for populating the table view, or it can use it to fetch or otherwise obtain the necessary data. When you design your view controller subclass, you should define a property to hold this data.
    View controllers should not obtain the data for their table view through a global variable or a singleton object such as the app delegate. Such direct dependencies make your code less reusable and more difficult to test and debug.
  • The current view controller on top of the navigation-controller stack creates the next view controller in the sequence and, before it pushes it onto the stack, sets the data that this view controller, acting as data source, needs to populate its table view.

  • 一个 视图控制器 (通常是UITableViewController一个子类),扮演着数据源的角色 ,使用表示数据层次级别的对象的数据填充其表格视图。
    当表视图显示项目列表时,该对象通常是一个数组。 当表视图显示项目详细信息(即数据层次结构的叶节点)时,该对象可以是自定义模型对象,核心数据受管对象,字典或类似的东西。

  • 视图控制器存储它填充其表格视图所需的数据。
    视图控制器可以直接使用这些数据来填充表视图,或者可以使用它来获取或获取必要的数据。 当你设计你的视图控制器子类时,你应该定义一个属性来保存这些数据。
    视图控制器不应通过全局变量或类似app代理的单例,获取其表视图的数据。 这种直接的依赖性使得你的代码更少重用,更难以测试和调试。
  • 导航控制器堆栈顶部的当前视图控制器将按顺序创建下一个视图控制器,并在将其压入栈之前,设置作为数据源的此视图控制器需要填充其表视图的数据。

Creating and Configuring a Table View(创建和配置表格视图)

Your app must present a table view to users before it can manage it in response to taps on rows and other actions. This chapter shows what you must do to create a table view, configure it, and populate it with data.

您的应用必须先向用户展示表格视图,然后才能对其进行管理以响应行和其他操作的点击。 本章介绍了创建表视图,配置它并使用数据填充必须执行的操作。

Most of the code examples shown in this chapter come from the sample projects UITableView Fundamentals for iOS and TheElements.

本章中显示的大多数代码示例都来自iOS和TheElements的示例项目UITableView Fundamentals

Basics of Table View Creation(表视图创建的基础知识)

To create a table view, several entities in an app must interact: the view controller, the table view itself, and the table view’s data source and delegate. The view controller, data source, and delegate are usually the same object. The view controller starts the calling sequence, diagrammed in Figure 4-1.

要创建表视图,应用中的几个实体必须进行交互:视图控制器,表视图本身和表视图的 数据源和代理 。 视图控制器,数据源和代理通常是同一个对象。 视图控制器启动调用序列, 如图4-1所示 。

  1. The view controller creates a UITableView instance in a certain frame and style. It can do this either programmatically or in a storyboard. The frame is usually set to the screen frame, minus the height of the status bar or, in a navigation-based app, to the screen frame minus the heights of the status bar and the navigation bar. The view controller may also set global properties of the table view at this point, such as its autoresizing behavior or a global row height.
    To learn how to create table views in a storyboard and programmatically, see Creating a Table View Using a Storyboardand Creating a Table View Programmatically.
  2. The view controller sets the data source and delegate of the table view and sends a reloadData message to it. The data source must adopt the UITableViewDataSource protocol, and the delegate must adopt the UITableViewDelegateprotocol.
  3. The data source receives a numberOfSectionsInTableView: message from the UITableView object and returns the number of sections in the table view. Although this is an optional protocol method, the data source must implement it if the table view has more than one section.
  4. For each section, the data source receives a tableView:numberOfRowsInSection: message and responds by returning the number of rows for the section.
  5. The data source receives a tableView:cellForRowAtIndexPath: message for each visible row in the table view. It responds by configuring and returning a UITableViewCell object for each row. The UITableView object uses this cell to draw the row.

  6. 视图控制器以特定的框架和样式创建UITableView实例。 它可以以编程方式或在故事板中执行此操作。 框架通常设置为屏幕框架,减去状态栏的高度,或者在基于导航的应用中,屏幕框架减去状态栏和导航栏的高度。 视图控制器也可以在此处设置表视图的全局属性,例如其自动调整行为或全局行高度。
    要了解如何在故事板中以编程方式创建表视图 ,请参阅使用故事板 创建表视图以编程方式创建表视图

  7. 视图控制器设置表视图的数据源和代理并向其发送reloadData消息。 数据源必须采用UITableViewDataSource协议,并且委托必须采用UITableViewDelegate协议。
  8. 数据源从UITableView对象接收一个numberOfSectionsInTableView:消息,并返回表视图中的部分数量。 尽管这是一种可选的协议方法,但如果表视图具有多个部分,数据源必须实现它。
  9. 对于每个部分,数据源都会收到一个tableView:numberOfRowsInSection:消息,并通过返回该部分的行数进行响应。
  10. 数据源接收表视图中每个可见行的tableView:cellForRowAtIndexPath:消息。 它通过为每一行配置并返回一个UITableViewCell对象来响应。 UITableView对象使用这个单元格绘制行。

Figure 4-1  Calling sequence for creating and configuring a table view

The diagram in Figure 4-1 shows the required protocol methods as well as the numberOfSectionsInTableView: method. Populating the table view with data occurs in steps 3 through 5. To learn how to implement the methods in these steps, see Populating a Dynamic Table View with Data.

图4-1中的图表显示了所需的 协议 方法以及numberOfSectionsInTableView:方法。 使用数据填充表视图出现在步骤3到5.要了解如何在这些步骤中实现方法,请参阅使用数据填充动态表视图

The data source and the delegate may implement other optional methods of their protocols to further configure the table view. For example, the data source might want to provide titles for each of the sections in the table view by implementing the tableView:titleForHeaderInSection: method. For more on some of these optional table view customizations, see Optional Table View Configurations.

数据源和代理可以实现其协议的其他可选方法来进一步配置表视图。 例如,数据源可能希望通过实现tableView:titleForHeaderInSection:方法为表视图中的每个部分提供标题。 有关这些可选表格视图自定义的更多信息,请参阅可选表格视图配置

You create a table view in either the plain style (UITableViewStylePlain) or the grouped style (UITableViewStyleGrouped). (You specify the style in a storyboard.) Although the procedure for creating a table view in either styles is identical, you may want to perform different kinds of configurations. For example, because a grouped table view generally presents item detail, you may also want to add custom accessory views (for example, switches and sliders) or custom content (for example, text fields). For an example, see A Closer Look at Table View Cells.

您可以使用普通样式( UITableViewStylePlain )或分组样式( UITableViewStyleGrouped )创建表视图。 (您可以在故事板中指定样式。)尽管用两种样式创建表格视图的过程都是相同的,但您可能需要执行不同类型的配置。 例如,由于分组表格视图通常会显示项目详细信息,因此您可能还需要添加自定义附件视图(例如开关和滑块)或自定义内容(例如文本字段)。 有关示例,请参阅仔细查看表格视图单元格

Recommendations for Creating and Configuring Table Views(有关创建和配置表格视图的建议)

There are many ways to put together a table view app. For example, you can use an instance of a custom NSObject subclass to create, configure, and manage a table view. However, you will find the task much easier if you adopt the classes, techniques, and design patterns that the UIKit framework offers for this purpose. The following approaches are recommended:

有很多方法可以组合一个表格视图应用程序。 例如,您可以使用自定义NSObject子类的实例来创建,配置和管理表格视图。 但是,如果您采用UIKit框架为此提供的类,技术和设计模式,您会发现该任务更容易。 建议采用以下方法:

  • Use an instance of a subclass of UITableViewController to create and manage a table view.
    Most apps use a custom UITableViewController object to manage a table view. As described in Navigating a Data Hierarchy with Table Views, UITableViewController automatically creates a table view, assigns itself as both delegate and data source (and adopts the corresponding protocols), and initiates the procedure for populating the table view with data. It also takes care of several other “housekeeping” details of behavior. The behavior of UITableViewController (a subclass of UIViewController) within the navigation controller architecture is described in Table View Controllers.
  • 使用UITableViewController子类的一个实例来创建和管理一个表视图。
    大多数应用程序使用自定义的UITableViewController对象来管理表格视图。 如使用表视图浏览数据层次结构中所述, UITableViewController自动创建一个表视图,将它自己分配为 代表和数据源 (并采用相应的 协议 ),并启动用数据填充表视图的过程。 它还处理其他几个“内务”行为细节。 表视图控制器中描述了导航控制器体系结构内UITableViewController ( UIViewController的子类)的行为。
  • If your app is largely based on table views, select the Master-Detail Application template provided by Xcode when you create your project.
    As described in Creating a Table View Using a Storyboard, the template includes stub code and a storyboard defining an app delegate, the navigation controller, and the master view controller (which is an instance of a custom subclass of UITableViewController).
  • 如果您的应用程序主要基于表视图,请在创建项目时选择由Xcode提供的主 - 细节应用程序模板。
    如使用故事板创建表视图中所述 ,模板包含存根代码和a 故事板 定义一个应用程序委托,导航控制器和主控 视图控制器 (这是UITableViewController的一个自定义子类的实例)。
  • For successive table views, you should implement custom UITableViewController objects. You can either load them from a storyboard or create the associated table views programmatically.
    Although either option is possible, the storyboard route is generally easier.
  • 对于连续的表视图,您应该实现自定义的UITableViewController对象。 您可以从故事板加载它们或以编程方式创建关联的表格视图。
    虽然任何一种选择都是可能的,故事板路线通常更容易。

If the view to be managed is a composite view in which a table view is one of multiple subviews, you must use a custom subclass of UIViewController to manage the table view (and other views). Do not use UITableViewController, because this controller class sizes the table view to fill the screen between the navigation bar and the tab bar (if either is present).

如果要管理的视图是一个复合视图,其中表视图是多个子视图中的一个,则必须使用UIViewController的自定义子类来管理表视图(和其他视图)。 不要使用UITableViewController ,因为此控制器类调整表格视图的大小以填充导航栏和选项卡栏(如果存在)之间的屏幕。

Creating a Table View Using a Storyboard(使用故事板创建表格视图)

Create an app with a table view using Xcode. When you create your project, select a template that contains stub code and a storyboard that, by default, supply the structure for setting up and managing table views.

使用Xcode创建具有表格视图的应用程序。 在创建项目时,选择一个包含存根代码和故事板的模板, 默认情况下,提供用于设置和管理表格视图的结构。

To create an app structured around table views

创建围绕表视图构建的应用程序

  1. In Xcode, choose File > New > Project.
  2. In the iOS section at the left side of the dialog, select Application.
  3. In the main area of the dialog, select Master-Detail Application and then click Next.
  4. Choose your project options (make sure Use Storyboard is selected), and then click Next.
  5. Choose a save location for your project and then click Create.

  6. 在Xcode中,选择File> New> Project。

  7. 在对话框左侧的iOS部分中,选择应用程序。
  8. 在对话框的主要区域中,选择主从应用程序,然后单击下一步。
  9. 选择您的项目选项(确保选择使用故事板),然后单击下一步。
  10. 为您的项目选择保存位置,然后单击创建。

Depending on which device family you chose in step 4, the project has one or two storyboards. To display the storyboard canvas, double-click a storyboard file in the project navigator. If the device family is iPhone, for example, your storyboard should contain a table view controller that looks similar to the one in Figure 4-2.

根据您在步骤4中选择的设备系列,该项目有一个或两个故事板。 要显示故事板画布,请双击项目导航器中的故事板文件。 例如,如果设备系列是iPhone,那么故事板应包含一个与图4-2中类似的表格视图控制器。

Figure 4-2  The master view controller in the Master-Detail Application storyboard

To make sure that the scene on the canvas represents the master view controller class in your code

确保画布上的场景表示代码中的主视图控制器类

  1. On the canvas, click the scene’s title bar to select the table view controller.
  2. Click the Identity button at the top of the utility area to open the Identity inspector.
  3. Verify that the Class field contains the project’s custom subclass of UITableViewController.

  4. 在画布上,单击场景的标题栏以选择表格视图控制器。

  5. 单击实用工具区域顶部的“标识”按钮以打开“标识”检查器。
  6. 验证Class字段是否包含项目的UITableViewController的自定义子类。

Choose the Table View’s Display Style(选择表格视图的显示样式)

As described in Table View Styles, every table view has a display style: plain or grouped.

如表视图样式中所述 ,每个表视图都有一种显示样式:纯文本或分组。

To choose the display style of a table view in a storyboard

在故事板中选择表格视图的显示风格

  1. Click the center of the scene to select the table view.
  2. In the utility area, display the Attributes inspector.
  3. In the Table View section of the Attributes inspector, use the Style pop-up menu to choose Plain or Grouped.

  4. 单击场景的中心以选择表格视图。

  5. 在实用程序区域中,显示“属性”检查器。
  6. 在“属性”检查器的“表格视图”部分中,使用“样式”弹出式菜单选择“平铺”或“分组”。

Choose the Table View’s Content Type(选择表格视图的内容类型)

Storyboards introduce two convenient ways to design a table view’s content:

故事板介绍了设计表格视图内容的两种便捷方式:

  • Dynamic prototypes. Design a prototype cell and then use it as the template for other cells in the table. Use a dynamic prototype when multiple cells in a table should use the same layout to display information. Dynamic content is managed by the table view data source (the table view controller) at runtime, with an arbitrary number of cells. Figure 4-3 shows a plain table view with a one prototype cell.

  • 动态原型 。 设计一个原型单元格,然后将其用作表格中其他单元格的模板。 当表中的多个单元格应使用相同的布局来显示信息时,请使用动态原型。 动态内容在运行时由表视图数据源(表格视图控制器)管理,并具有任意数量的单元格。 图4-3显示了具有一个原型单元格的普通表格视图。

Figure 4-3  A dynamic table view

Note: If a table view in a storyboard is dynamic, the custom subclass of UITableViewController that contains the table view needs to implement the data source protocol. For more information, see Populating a Dynamic Table View with Data.

注意:如果故事板中的表视图是动态的,则包含表视图的UITableViewController的自定义子类需要实现数据源协议。 有关更多信息,请参阅使用数据填充动态表视图 。

  • Static cells. Use static content to design the overall layout of the table, including the total number of cells. A table view with static content has a fixed set of cells that you can configure at design time. You can also configure other static data elements such as section headers. Use static cells when a table does not change its layout, regardless of the specific information it displays. Figure 4-4 shows a grouped table view with three static cells.

  • 静态单元格 。 使用静态内容来设计表格的总体布局,包括单元格的总数。 具有静态内容的表格视图具有一组固定的单元格,您可以在设计时配置这些单元格。 您还可以配置其他静态数据元素,例如节标题。 无论显示的具体信息如何,表格都不会更改其布局时使用静态单元格。 图4-4显示了具有三个静态单元格的分组表格视图。

Figure 4-4  A static table view

Note: If a table view in a storyboard is static, the custom subclass of UITableViewController that contains the table view should not implement the data source protocol. Instead, the table view controller should use its viewDidLoadmethod to populate the table view’s data. For more information, see Populating a Static Table View With Data.

注意:如果故事板中的表视图是静态的,则包含表视图的UITableViewController的自定义子类不应实现数据源协议。 相反,表视图控制器应该使用其viewDidLoad方法来填充表视图的数据。 有关更多信息,请参阅使用数据填充静态表视图 。

By default, when you add a table view controller to a storyboard, the controller contains a table view that uses prototype-based cells. If you want to use static cells:

默认情况下,将表视图控制器添加到故事板时,控制器包含使用基于原型的单元格的表视图。 如果你想使用静态单元格:

  1. Select the table view.
  2. Display the Attributes inspector.
  3. In the the Content pop-up menu, choose Static Cells.

  4. 选择表格视图。

  5. 显示属性检查器。
  6. 在“内容”弹出菜单中,选择“静态单元”。

If you’re designing a prototype cell, the table view needs a way to identify the prototype when the data source dequeues reusable cells for the table at runtime. You do this by assigning a reuse identifier to the cell. In the Table View Cell section of the Attributes inspector, enter a string in the Identifier text field, which you will also use when asking for a new cell of that type. To make understanding the code easier, a cell’s reuse identifier should describe what the cell contains. For example, a cell for displaying bird sightings might have an identifier of @”BirdSightingCell”.

如果您正在设计原型单元格,那么当数据源在运行时将表的可重用单元出列时,表视图需要一种方法来标识原型。 您可以通过为单元分配重用标识符来完成此操作。 在“属性”检查器的“表格视图单元”部分中,在“标识符”文本字段中输入一个字符串,在询问该类型的新单元格时也会使用该字符串。 为了更容易理解代码,单元的重用标识符应该描述单元包含的内容。 例如,用于显示鸟类目击的单元可能具有@”BirdSightingCell”的标识符。

Design the Table View’s Rows(设计表格视图的行)

As described in Standard Styles for Table View Cells, UIKit defines four styles for the cells that a table view uses to draw its rows. You can use one of the four standard styles, design a custom style, or subclass UITableViewCell to define additional behavior or properties for the cell. This topic is covered in detail in A Closer Look at Table View Cells.

如表格视图单元格的标准样式中所述,UIKit为表格视图用于绘制行的单元格定义了四种样式。 您可以使用四种标准样式之一,设计自定义样式或UITableViewCell子类来定义单元格的其他行为或属性。 在“仔细查看表格视图单元格”中详细介绍了此主题。

A table view cell can also have an accessory, as described in Accessory Views. An accessory is a standard user interface element that UIKit draws at the right end of a table cell. For example, the disclosure indicator, which looks similar to a right angle bracket (>), tells users that tapping an item reveals related information in a new screen. In the Attributes inspector, use the Accessory pop‐up menu to select a cell’s accessory.

表格视图单元格也可以具有附件,如附件视图中所述 。 附件是UIKit在表格单元右端绘制的标准用户界面元素。 例如,披露指标与右尖括号(>)相似,告诉用户点击一个项目会在新屏幕中显示相关信息。 在“属性”检查器中,使用“附件”弹出式菜单选择单元的附件。

Create Additional Table Views(创建附加的表格视图)

If your app displays and manages more than one table view, add those table views to your storyboard. You add a table view by adding a custom UITableViewController object, which contains the table view it manages.

如果您的应用显示并管理多个表格视图,请将这些表格视图添加到故事板。 你可以通过添加一个自定义的UITableViewController对象来添加一个表格视图,该对象包含它所管理的表格视图。

To add custom class files to your project

将自定义类文件添加到您的项目

  1. In Xcode, choose File > New > File.
  2. In the iOS section at the left side of the dialog, select Cocoa Touch.
  3. In the main area of the dialog, select Objective-C class, and then click Next.
  4. Enter a name for your new class, choose subclass of UITableViewController, and then click Next.
  5. Choose a save location for your class files, and then click Create.

  6. 在Xcode中,选择File> New> File。

  7. 在对话框左侧的iOS部分,选择Cocoa Touch。
  8. 在对话框的主要区域中,选择Objective-C类,然后单击下一步。
  9. 输入新类的名称,选择UITableViewController子类,然后单击下一步。
  10. 为班级文件选择保存位置,然后单击创建。

To add a table view controller to a storyboard

将一个表格视图控制器添加到故事板

  1. Display the storyboard to which you want to add the table view controller.
  2. Drag a table view controller out of the object library and drop it on the storyboard.
  3. With the new scene still selected on the canvas, click the Identity button in the utility area to open the Identity inspector.
  4. In the Custom Class section, choose the new custom class in the Class pop-up menu.
  5. Set the new table view’s style and cell content (dynamic or static).
  6. Create a segue to the new scene.

  7. 显示要添加表格视图控制器的故事板。

  8. 将表格视图控制器拖出对象库并将其放置在故事板上。
  9. 在画布上仍选择新场景后,单击实用工具区中的“标识”按钮以打开“标识”检查器。
  10. 在“自定义类”部分中,在“类”弹出菜单中选择新的自定义类。
  11. 设置新表格视图的样式和单元格内容(动态或静态)。
  12. 为新场景创建一个segue。

The details of step 7 vary depending on the project. To learn more about adding segues, see Xcode User Guide.

步骤7的细节因项目而异。 要了解有关添加赛段的更多信息,请参阅 Xcode用户指南 。

Note: Populating a table view with data and configuring a table view are discussed in Populating a Dynamic Table View with Data and Optional Table View Configurations.

注意:在使用数据和可选表视图配置 填充动态表视图中讨论使用数据 填充表视图和配置表视图 。

Learn More by Creating a Sample App(通过创建示例应用程序了解更多信息)

The tutorial Your Second iOS App: Storyboards shows how to create a sample app that is structured around table views. After you complete the steps in this tutorial, you’ll have a working knowledge of how to create both dynamic and static table views using a storyboard. The tutorial creates a basic navigation-based app called BirdWatching that uses table view controllers connected by both push and modal segues.

教程 你的第二个iOS应用程序:故事板 展示了如何创建一个围绕表视图构建的示例应用程序。 在完成本教程中的步骤之后,您将掌握如何使用故事板创建动态和静态表视图的工作知识。 本教程创建了一个名为BirdWatching的基本导航应用程序,该应用程序使用通过推式和模态连接连接的表视图控制器。

Creating a Table View Programmatically(以编程方式创建表视图)

If you choose not to use UITableViewController for table view management, you must replicate what this class gives you “for free.”

如果您选择不使用UITableViewController进行表视图管理,则必须“免费”复制此类为您提供的内容。

Adopt the Data Source and Delegate Protocols(采用数据源和代理协议)

The class creating the table view typically makes itself the data source and delegate by adopting the UITableViewDataSourceand UITableViewDelegate protocols. The adoption syntax appears just after the superclass in the @interface directive, as shown in Listing 4-1.

创建表视图的类通常会自行创建 ,数据源和代理 通过采用UITableViewDataSource和UITableViewDelegate 协议 。 采用语法出现在@interface指令中的超类之后,如清单4-1所示。

Listing 4-1 Adopting the data source and delegate protocols

@interface RootViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) NSArray *timeZoneNames;
@end

Create and Configure a Table View(创建和配置表格视图)

The next step is for the client to allocate and initialize an instance of the UITableView class. Listing 4-2 gives an example of a client that creates a UITableView object in the plain style, specifies its autoresizing characteristics, and then sets itself to be both data source and delegate. Again, keep in mind that the UITableViewController does all of this for you automatically.

下一步是客户 分配和初始化 UITableView类的一个实例。 清单4-2给出了一个客户端示例,该客户端以普通样式创建UITableView对象,指定其自动识别特征,然后将自身设置为数据源和代理。 再次,请记住, UITableViewController会自动为您完成所有这些工作。

Listing 4-2 Creating a table view

- (void)loadView
{
    UITableView *tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
    tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
    tableView.delegate = self;
    tableView.dataSource = self;
    [tableView reloadData];

    self.view = tableView;
}

Because in this example the class creating the table view is a subclass of UIViewController, it assigns the created table view to its view property, which it inherits from that class. It also sends a reloadData message to the table view, causing the table view to initiate the procedure for populating its sections and rows with data.

因为在这个例子中,创建表视图的类是UIViewController一个子类,它将创建的表视图分配给它的view属性,它从该类继承。 它还向表视图发送reloadData消息,使表视图启动用数据填充其部分和行的过程。

Populating a Dynamic Table View with Data(用数据填充动态表格视图)

Just after a table view object is created, it receives a reloadData message, which tells it to start querying the data source and delegate for the information it needs for the sections and rows it displays. The table view immediately asks the data source for its logical dimensions—that is, the number of sections and the number of rows in each section. It then repeatedly invokes the tableView:cellForRowAtIndexPath: method to get a cell object for each visible row; it uses this UITableViewCell object to draw the content of the row. (Scrolling a table view also causes an invocation of tableView:cellForRowAtIndexPath: for each newly visible row.)

在创建表视图对象之后,它会收到一个reloadData消息,告诉它开始查询 数据源和委托 获取它显示的部分和行所需的信息。 表视图立即要求数据源的逻辑维度 - 即每个部分中的部分数量和行数。 然后它重复调用tableView:cellForRowAtIndexPath:方法为每个可见行获取一个单元格对象; 它使用这个UITableViewCell对象来绘制行的内容。 (滚动表视图也会导致调用tableView:cellForRowAtIndexPath:用于每个新显示的行。)

As noted in Choose the Table View’s Content Type, if the table view is dynamic then you need to implement the required data source methods. Listing 4-3 shows an example of how the data source and the delegate could configure a dynamic table view.

如选择表视图的内容类型所述 ,如果表视图是动态的,那么您需要实现所需的数据源方法。 清单4-3显示了数据源和代理如何配置动态表视图的示例。

Listing 4-3 Populating a dynamic table view with data

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [regions count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Number of rows is the number of time zones in the region for the specified section.
    Region *region = [regions objectAtIndex:section];
    return [region.timeZoneWrappers count];
}


- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    // The header for the section is the region name -- get this from the region at the section index.
    Region *region = [regions objectAtIndex:section];
    return [region name];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *MyIdentifier = @"MyReuseIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault  reuseIdentifier:MyIdentifier];
    }
    Region *region = [regions objectAtIndex:indexPath.section];
    TimeZoneWrapper *timeZoneWrapper = [region.timeZoneWrappers objectAtIndex:indexPath.row];
    cell.textLabel.text = timeZoneWrapper.localeName;
    return cell;
}

The data source, in its implementation of the tableView:cellForRowAtIndexPath: method, returns a configured cell object that the table view can use to draw a row. For performance reasons, the data source tries to reuse cells as much as possible. It first asks the table view for a specific reusable cell object by sending it a dequeueReusableCellWithIdentifier: message. If no such object exists, the data source creates it, assigning it a reuse identifier. The data source sets the cell’s content (in this example, its text) and returns it. A Closer Look at Table View Cells discusses this data source method and UITableViewCellobjects in more detail.

数据源在它的tableView:cellForRowAtIndexPath:方法的实现中返回一个已配置的单元对象,表视图可以用来绘制一行。 出于性能原因,数据源尽可能地尝试重用单元。 它首先通过向特定的可重用单元对象发送一个dequeueReusableCellWithIdentifier:消息来请求表视图。 如果不存在这样的对象,则数据源创建它,并为其分配一个重用标识符。 数据源设置单元格的内容(在本例中为文本)并返回它。 仔细观察Table View Cells将更详细地讨论这个数据源方法和UITableViewCell对象。

If the dequeueReusableCellWithIdentifier: method asks for a cell that’s defined in a storyboard, the method always returns a valid cell. If there is not a recycled cell waiting to be reused, the method creates a new one using the information in the storyboard itself. This eliminates the need to check the return value for nil and create a cell manually.

如果dequeueReusableCellWithIdentifier:方法要求在故事板中定义的单元格,该方法始终返回有效的单元格。 如果没有等待重复使用的再循环单元格,该方法将使用故事板本身中的信息创建一个新单元格。 这消除了检查nil的返回值并手动创建单元的需要。

The implementation of the tableView:cellForRowAtIndexPath: method in Listing 4-3 includes an NSIndexPath argument that identifies the table view section and row. UIKit declares a category of the NSIndexPath class, which is defined in the Foundation framework. This category extends the class to enable the identification of table view rows by section and row number. For more information on this category, see NSIndexPath UIKit Additions.

清单4-3中的tableView:cellForRowAtIndexPath:方法的实现包含一个标识表视图部分和行的NSIndexPath参数。 UIKit声明了一个 类别 在Foundation框架中定义的NSIndexPath类。 此类别扩展了该类,以便按部分和行号标识表格视图行。 有关此类别的更多信息,请参阅 NSIndexPath UIKit添加 。

Populating a Static Table View With Data(使用数据填充静态表格视图)

As noted in Choose the Table View’s Content Type, if a table view is static then you should not implement any data source methods. The configuration of the table view is known at compile time, so UIKit can get this information from the storyboard at runtime. However, you still need to populate a static table view with data from your data model. Populating a Static Table View With Data shows an example of how a table view controller could load user data in a static table view. This example is adapted from Your Second iOS App: Storyboards.

如选择表视图的内容类型所述 ,如果表视图是静态的,那么您不应该实现任何数据源方法。 表视图的配置在编译时已知,所以UIKit可以在运行时从故事板获取这些信息。 但是,您仍然需要使用数据模型中的数据填充静态表视图。 使用Data填充静态表视图显示了表视图控制器如何在静态表视图中加载用户数据的示例。 这个例子改编自 你的第二个iOS应用程序:故事板 。

Listing 4-4 Populating a static table view with data

- (void)viewDidLoad
{
    [super viewDidLoad];

    BirdSighting *theSighting = self.sighting;
    static NSDateFormatter *formatter = nil;
    if (formatter == nil) {
        formatter = [[NSDateFormatter alloc] init];
        [formatter setDateStyle:NSDateFormatterMediumStyle];
    }
    if (theSighting) {
        self.birdNameLabel.text = theSighting.name;
        self.locationLabel.text = theSighting.location;
        self.dateLabel.text = [formatter stringFromDate:(NSDate*)theSighting.date];
    }
}

The table view is populated with data in the UIViewController method viewDidLoad, which is called after the view is loaded into memory. The data is passed to the table view controller in the sighting object, which is set in the previous view controller’s prepareForSegue:sender: method. The properties birdNameLabel, locationLabel, and dateLabel are outlets connected to labels in the static table view (see Figure 4-4).

表视图用UIViewController方法中的数据填充, viewDidLoad在视图加载到内存后调用。 数据被传递给sighting对象中的表格视图控制器,该目标在前面的视图控制器的prepareForSegue:sender:方法中设置。 属性birdNameLabel , locationLabel和dateLabel是连接到静态表视图中标签的出口(参见图4-4 )。

Populating an Indexed List(填充索引列表)

An indexed list (see Figure 1-2) is ideally suited for navigating large amounts of data organized by a conventional ordering scheme such as an alphabet. An indexed list is a table view in the plain style that is specially configured through three UITableViewDataSource methods:

一个索引列表( 见图1-2 )非常适合用于浏览由传统排序方案(如字母表)组织的大量数据。 索引列表是通过三种UITableViewDataSource方法特别配置的普通样式的表视图:

  • sectionIndexTitlesForTableView:
    Returns an array of the strings to use as the index entries (in order).
  • tableView:titleForHeaderInSection:
    Maps these index strings to the titles of the table view’s sections (they don’t have to be the same).
  • tableView:sectionForSectionIndexTitle:atIndex:
    Returns the section index related to the entry the user tapped in the index.

  • sectionIndexTitlesForTableView:
    返回要用作索引条目的字符串数组(按顺序)。

  • tableView:titleForHeaderInSection:
    将这些索引字符串映射到表格视图各节的标题(它们不必相同)。
  • tableView:sectionForSectionIndexTitle:atIndex:
    返回与用户在索引中轻敲的条目相关的部分索引。

The data you use to populate an indexed list should be organized to reflect this indexing model. Specifically, you need to build an array of arrays. Each inner array corresponds to a section in the table. Section arrays are sorted (or collated) within the outer array according to the prevailing ordering scheme, which is often an alphabetical scheme (for example, A through Z). Additionally, the items in each section array are sorted. You can build and sort this array of arrays yourself, but fortunately the UILocalizedIndexedCollation class greatly simplifies the tasks of building and sorting these data structures and providing data to the table view. The class also collates items in the arrays according to the current localization.

您用来填充索引列表的数据应该组织起来以反映这种索引模式。 具体来说,你需要建立一个 数组的数组 。 每个内部数组对应于表中的一个部分。 根据主要的排序方案(通常是按字母顺序排列的方案(例如,A到Z)),在外部数组内对部分数组进行排序(或整理)。 此外,每个部分数组中的项目都是排序的。 您可以自己构建和排序此数组的数组,但幸运的是, UILocalizedIndexedCollation类大大简化了构建和排序这些数据结构以及向表视图提供数据的任务。 根据当前的本地化,该类还会整理数组中的项目。

However you internally manage this array-of-arrays structure is up to you. The objects to be collated should have a property or method that returns a string value that the UILocalizedIndexedCollation class uses in collation; if it is a method, it should have no parameters. You might find it convenient to define a custom model class whose instances represent the rows in the table view. These model objects not only return a string value but also define a property that holds the index of the section array to which the object is assigned. Listing 4-5 illustrates the definition of a class that declares a name property and a sectionNumber property.

但是,您在内部管理此阵列结构取决于您。 要整理的对象应该有一个 属性 或返回UILocalizedIndexedCollation类在整理中使用的字符串值的方法; 如果它是一种方法,它应该没有参数。 您可能会发现定义自定义模型类很方便 , 其实例表示表视图中的行。 这些模型对象不仅返回一个字符串值,而且还定义一个属性,该属性保存该对象分配给的节数组的索引。 清单4-5说明了声明name属性和sectionNumber属性的类的定义。

Listing 4-5 Defining the model-object interface

@interface State : NSObject

@property(nonatomic,copy) NSString *name;
@property(nonatomic,copy) NSString *capitol;
@property(nonatomic,copy) NSString *population;
@property NSInteger sectionNumber;
@end

Before your table view controller is asked to populate the table view, you load the data to be used (from whatever source) and create instances of your model class from this data. The example in Listing 4-6 loads data defined in a property list and creates the model objects from that. It also obtains the shared instance of UILocalizedIndexedCollation and initializes the mutablearray (states) that will contain the section arrays.

在你的表格视图控制器之前, 被要求填充表视图,加载要使用的数据(来自任何源),并从这些数据创建模型类的实例。 清单4-6中的示例加载在属性列表中定义的数据并从中创建模型对象。 它还获得UILocalizedIndexedCollation的共享实例,以及 初始化 该 易变的 数组( states ),将包含section数组。

Listing 4-6 Loading the table-view data and initializing the model objects

- (void)viewDidLoad {
    [super viewDidLoad];
    UILocalizedIndexedCollation *theCollation = [UILocalizedIndexedCollation currentCollation];
    self.states = [NSMutableArray arrayWithCapacity:1];

    NSString *thePath = [[NSBundle mainBundle] pathForResource:@"States" ofType:@"plist"];
    NSArray *tempArray;
    NSMutableArray *statesTemp;
    if (thePath && (tempArray = [NSArray arrayWithContentsOfFile:thePath]) ) {
        statesTemp = [NSMutableArray arrayWithCapacity:1];
        for (NSDictionary *stateDict in tempArray) {
            State *aState = [[State alloc] init];
            aState.name = [stateDict objectForKey:@"Name"];
            aState.population = [stateDict objectForKey:@"Population"];
            aState.capitol = [stateDict objectForKey:@"Capitol"];
            [statesTemp addObject:aState];
        }
    } else  {
        return;
    }

After the data source has this “raw” array of model objects, it can process it with the facilities of the UILocalizedIndexedCollation class. In Listing 4-7, the code is annotated with numbers.

之后 数据源 有这个“原始”模型对象数组,它可以使用UILocalizedIndexedCollation类的工具来处理它。 在程序清单4-7中 ,代码用数字注释。

Listing 4-7 Preparing the data for the indexed list

    // viewDidLoad continued...
    // (1)
    for (State *theState in statesTemp) {
        NSInteger sect = [theCollation sectionForObject:theState collationStringSelector:@selector(name)];
        theState.sectionNumber = sect;
    }
    // (2)
    NSInteger highSection = [[theCollation sectionTitles] count];
    NSMutableArray *sectionArrays = [NSMutableArray arrayWithCapacity:highSection];
    for (int i = 0; i < highSection; i++) {
        NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:1];
        [sectionArrays addObject:sectionArray];
    }
    // (3)
    for (State *theState in statesTemp) {
        [(NSMutableArray *)[sectionArrays objectAtIndex:theState.sectionNumber] addObject:theState];
    }
    // (4)
    for (NSMutableArray *sectionArray in sectionArrays) {
        NSArray *sortedSection = [theCollation sortedArrayFromArray:sectionArray
            collationStringSelector:@selector(name)];
        [self.states addObject:sortedSection];
    }
} // end of viewDidLoad

Here’s what the code in Listing 4-7 does:

以下是代码清单4-7中的代码行为:

  1. The data source enumerates the array of model objects and sends sectionForObject:collationStringSelector: to the collation manager on each iteration. This method takes as arguments a model object and a property or method of the object that it uses in collation. Each call returns the index of the section array to which the model object belongs, and that value is assigned to the sectionNumber property.
  2. The data source source then creates a (temporary) outer mutable array and mutable arrays for each section; it adds each created section array to the outer array.
  3. It then enumerates the array of model objects and adds each object to its assigned section array.
  4. The data source enumerates the array of section arrays and calls sortedArrayFromArray:collationStringSelector:on the collation manager to sort the items in each array. It passes in a section array and a property or method that is to be used in sorting the items in the array. Each sorted section array is added to the final outer array.

  5. 数据源枚举模型对象的数组,并在每次迭代中将sectionForObject:collationStringSelector:发送到排序管理器。 此方法将模型对象和它在排序中使用的对象的属性或方法作为参数。 每次调用都会返回模型对象所属的部分数组的索引,并将该值分配给sectionNumber属性。

  6. 数据源源然后为每个部分创建一个(临时)外部可变数组和可变数组; 它将每个创建的部分数组添加到外部数组。
  7. 然后枚举模型对象的数组并将每个对象添加到其分配的部分数组。
  8. 数据源枚举节数组的数组并调用sortedArrayFromArray:collationStringSelector:在排序管理器上对每个数组中的项进行排序。 它传递一个节数组和一个属性或方法,用于对数组中的项进行排序。 每个排序的部分数组被添加到最终的外部数组中。

Now the data source is ready to populate its table view with data. It implements the methods specific to indexed lists as shown in Listing 4-8. In doing this it calls two UILocalizedIndexedCollation methods: sectionIndexTitles and sectionForSectionIndexTitleAtIndex:. Also note that in tableView:titleForHeaderInSection: it suppresses any headers from appearing in the table view when the associated section does not have any items.

现在数据源已准备好用数据填充其表格视图。 它实现了特定于索引列表的方法,如清单4-8所示。 在这样做时,它会调用两个UILocalizedIndexedCollation方法: sectionIndexTitles和UILocalizedIndexedCollation 另请注意,在tableView:titleForHeaderInSection:当相关部分没有任何项目时,它禁止任何标题出现在表格视图中。

Listing 4-8 Providing section-index data to the table view

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if ([[self.states objectAtIndex:section] count] > 0) {
        return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:section];
    }
    return nil;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}

Accessibility Note: To change what VoiceOver reads aloud when the indexed list is selected, assign a localized string to the accessibilityLabel property of each item in the array that sectionIndexTitlesForTableView: returns.

辅助功能注意:要在选择索引列表时更改VoiceOver朗读的内容,请将本地化字符串分配给sectionIndexTitlesForTableView:返回的数组中的每个项目的accessibilityLabel属性。

Finally, the data source should implement the UITableViewDataSource methods that are common to all table views. Listing 4-9 gives examples of these implementations, and illustrates how to use the section and row properties of the table view–specific category of the NSIndexPath class described in NSIndexPath UIKit Additions.

最后,数据源应该实现所有表视图通用的UITableViewDataSource方法。 清单4-9给出了这些实现的示例,并说明了如何使用特定于表视图的section和row属性 类别 ,描述的NSIndexPath类 NSIndexPath UIKit添加 。

Listing 4-9 Populating the rows of an indexed list

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.states count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[self.states objectAtIndex:section] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"StateCell";
    UITableViewCell *cell;
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    State *stateObj = [[self.states objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
    cell.textLabel.text = stateObj.name;
    return cell;
}

For table views that are indexed lists, when the data source assigns cells for rows in tableView:cellForRowAtIndexPath:, it should ensure that the accessoryType property of the cell is set to UITableViewCellAccessoryNone.

对于索引列表的表视图,当数据源为tableView:cellForRowAtIndexPath:行分配单元格时,应确保单元格的accessoryType属性设置为UITableViewCellAccessoryNone 。

After initially populating the table view following the procedure outlined above, you can reload the contents of the index by calling the reloadSectionIndexTitles method.

按照上述步骤初始填充表视图后,可以通过调用reloadSectionIndexTitles方法重新加载索引的内容。

Optional Table View Configurations(可选的表格视图配置)

The table view API allows you to configure various visual and behavioral aspects of a table view, including specific rows and sections. The following examples serve to give you some idea of the options available to you.

表视图API允许您配置表视图的各种视觉和行为方面,包括特定的行和部分。 以下示例可以帮助您了解可用的选项。

Add a Custom Title(添加自定义标题)

In the same block of code that creates the table view, you can apply global configurations using certain methods of the UITableView class. The code example in Listing 4-10 adds a custom title for the table view (using a UILabel object).

在创建表视图的相同代码块中,可以使用UITableView类的某些方法应用全局配置。 清单4-10中的代码示例为表视图添加了自定义标题(使用UILabel对象)。

Listing 4-10 Adding a title to the table view

- (void)loadView
{
    CGRect titleRect = CGRectMake(0, 0, 300, 40);
    UILabel *tableTitle = [[UILabel alloc] initWithFrame:titleRect];
    tableTitle.textColor = [UIColor blueColor];
    tableTitle.backgroundColor = [self.tableView backgroundColor];
    tableTitle.opaque = YES;
    tableTitle.font = [UIFont boldSystemFontOfSize:18];
    tableTitle.text = [curTrail objectForKey:@"Name"];
    self.tableView.tableHeaderView = tableTitle;
    [self.tableView reloadData];
}

Provide a Section Title(提供section标题)

The example in Listing 4-11 returns a title string for a section.

清单4-11中的例子返回一个section的标题字符串。

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    // Returns section title based on physical state: [solid, liquid, gas, artificial]
    return [[[PeriodicElements sharedPeriodicElements] elementPhysicalStatesArray] objectAtIndex:section];
}

Indent a Row(缩进一行)

The code in Listing 4-12 moves a specific row to the next level of indentation.

清单4-12中的代码将特定的行移动到下一级缩进。

Listing 4-12 Custom indentation of a row

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ( indexPath.section==TRAIL_MAP_SECTION && indexPath.row==0 ) {
        return 2;
    }
    return 1;
}

Vary a Row’s Height(改变行的高度)

The example in Listing 4-13 varies the height of a specific row based on its index value.

清单4-13中的示例根据索引值改变特定行的高度。

Listing 4-13 Varying row height

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat result;

    switch ([indexPath row])
    {
        case 0:
        {
            result = kUIRowHeight;
            break;
        }
        case 1:
        {
            result = kUIRowLabelHeight;
            break;
        }
    }
    return result;
}

Customize Cells(自定义单元格)

You can also affect the appearance of rows by returning custom UITableViewCell objects with specially formatted subviews for content in tableView:cellForRowAtIndexPath:. Cell customization is discussed in A Closer Look at Table View Cells.

您还可以通过返回UITableViewCell包含特殊格式子视图的自定义对象来影响行的外观tableView:cellForRowAtIndexPath:。细胞定制在“仔细观察表格视图细胞”中讨论。

A Closer Look at Table View Cells(仔细观察表格视图单元格)

A table view uses cell objects to draw its visible rows and then caches those objects as long as the rows are visible. Cells inherit from the UITableViewCell class. The table view’s data source provides the cell objects to the table view by implementing the tableView:cellForRowAtIndexPath: method, a required method of the UITableViewDataSource protocol.

表格视图使用单元格对象来绘制其可见行,然后只要这些行可见就缓存这些对象。 单元格从UITableViewCell类继承。 表视图的 数据源 通过实现tableView:cellForRowAtIndexPath:方法向UITableViewDataSource提供单元对象到表视图 协议 。

In this chapter, you’ll learn about:

在本章中,您将了解到:

  • The characteristics of cells
  • How to use the default capabilities of UITableViewCell for setting cell content
  • How to create custom UITableViewCell objects

  • 细胞的特征

  • 如何使用UITableViewCell的默认功能来设置单元格内容
  • 如何创建自定义的UITableViewCell对象

Characteristics of Cell Objects(cell对象的特征)

A cell object has various parts, which can change depending on the mode of the table view. Normally, most of a cell object is reserved for its content: text, image, or any other kind of distinctive identifier. Figure 5-1 shows the major parts of a cell.

单元对象具有各种部分,可根据表视图的模式进行更改。 通常,大部分单元格对象都是为其内容保留的:文本,图像或任何其他种类的独特标识符。 图5-1显示了一个单元的主要部分。

Figure 5-1  Parts of a table view cell

The smaller area on the right side of the cell is reserved for accessory views: disclosure indicators, detail disclosure controls, control objects such as sliders or switches, and custom views.

单元右侧的较小区域保留用于附件视图:披露指标,详细披露控制,控件对象(如滑块或开关)和自定义视图。

When the table view goes into editing mode, the editing control for each cell object (if it’s configured to have such a control) appears on its left side, in the area shown in Figure 5-2.

当表视图进入编辑模式时,每个单元对象的编辑控件(如果它被配置为具有这样的控件)出现在它的左侧, 如图5-2所示。

Figure 5-2  Parts of a table-view cell in editing mode

The editing control can be either a deletion control (a red minus sign inside a circle) or an insertion control (a green plus sign inside a circle). The cell’s content is pushed toward the right to make room for the editing control. If the cell object is configured for reordering (that is, relocation within the table view), the reordering control appears in the right side of the cell, next to any accessory view specified for editing mode. The reordering control is a stack of horizontal lines; to relocate a row within its table view, users press on the reordering control and drag the cell.

编辑控件可以是删除控件(圆内的红色减号)或插入控件(圆圈内的绿色加号)。 单元格的内容被推向右侧,为编辑控件腾出空间。 如果单元格对象配置为重新排序(即在表格视图中重新定位),则重排序控件将显示在单元格的右侧,旁边是为编辑模式指定的任何附件视图。 重新排序控制是一堆水平线; 要在其表格视图中重新定位行,用户按下重新排序控件并拖动单元格。

If a cell object is reusable—the typical case—you assign it a reuse identifier (an arbitrary string) in the storyboard. At runtime, the table view stores cell objects in an internal queue. When the table view asks the data source to configure a cell object for display, the data source can access the queued object by sending a dequeueReusableCellWithIdentifier: message to the table view, passing in a reuse identifier. The data source sets the content of the cell and any special properties before returning it. This reuse of cell objects is a performance enhancement because it eliminates the overhead of cell creation.

如果一个单元对象是可重用的 - 典型的情况 - 你在故事板中为它分配一个重用标识符(一个任意字符串)。 在运行时,表视图将单元对象存储在内部队列中。 当表视图询问 数据源要配置要显示的单元对象,数据源可以通过向表视图发送dequeueReusableCellWithIdentifier:消息来访问排队的对象,传入重用标识符。 数据源在返回之前设置单元格的内容和任何特殊属性。 细胞对象的这种重用是一种性能增强,因为它消除了细胞创建的开销。

With multiple cell objects in a queue, each with its own identifier, you can have table views constructed from cell objects of different types. For example, some rows of a table view can have content based on the image and text properties of a UITableViewCell in a predefined style, while other rows can be based on a customized UITableViewCell that defines a special format for its content.

由于队列中有多个单元对象,每个单元都有自己的标识符,因此可以使用不同类型的单元对象构建表视图。 例如,表视图的某些行可以具有基于预定义样式的UITableViewCell的图像和文本属性的内容,而其他行可以基于为其内容定义特殊格式的定制UITableViewCell 。

When providing cells for the table view, there are three general approaches you can take. You can use ready-made cell objects in a range of styles, you can add your own subviews to the cell object’s content view (which can be done in Interface Builder), or you can use cell objects created from a custom subclass of UITableViewCell. Note that the content view is a container of other views and so displays no content itself.

为表格视图提供单元格时,您可以采用三种常用方法。 您可以使用各种样式的现成单元对象,您可以将自己的子视图添加到单元对象的内容视图(可以在Interface Builder中完成),也可以使用从UITableViewCell的自定义子类创建的单元对象。 请注意,内容视图是其他视图的容器,因此不会显示任何内容。

Using Cell Objects in Predefined Styles(在预定义样式中使用单元格对象)

Using the UITableViewCell class directly, you can create “off-the-shelf” cell objects in a range of predefined styles. Standard Styles for Table View Cells describes these standard cells and provides examples of how they look in a table view. These cells are associated with the following enum constants, declared in UITableViewCell.h:

直接使用UITableViewCell类,您可以 创建 在一系列预定义样式中的“现成”单元对象。 表格视图单元格的标准样式描述了这些标准单元格,并提供了它们在表格视图中的外观示例。 这些单元格与在UITableViewCell.h声明的以下enum常量关联:

typedef enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
} UITableViewCellStyle;

These cell objects have two kinds of content: one or more text strings and, in some cases, an image. Figure 5-3 shows the approximate areas for image and text. As an image expands to the right, it pushes the text in the same direction.

这些单元格对象有两种内容:一个或多个文本字符串以及某些情况下的图像。 图5-3显示了图像和文本的大致区域。 随着图像向右扩展,它会将文本推向相同的方向。

Figure 5-3  Default cell content in a UITableViewCell object

The UITableViewCell class defines three properties for this cell content:

UITableViewCell类定义了三个 属性, 对于这个单元格内容:

  • textLabel—A label for the title (a UILabel object)
  • detailTextLabel—A label for the subtitle if there is additional detail (a UILabel object)
  • imageView—An image view for an image (a UIImageView object)

  • textLabel - 标题的标签(一个UILabel对象)

  • detailTextLabel - 如果有其他细节( UILabel对象),则为字幕标签
  • imageView - 一个图像的图像视图(一个UIImageView对象)

Because the first two of these properties are labels, you can set the font, alignment, line-break mode, and color of the associated text through the properties defined by the UILabel class (including the color of text when the row is highlighted). For the image view property, you can also set an alternative image for when the cell is highlighted using the highlightedImageproperty of the UIImageView class.

因为前两个属性是标签,所以可以通过UILabel类定义的属性(包括突出显示行时的文本颜色)设置相关文本的字体,对齐方式,换行方式和颜色。 对于图像视图属性,还可以使用UIImageView类的UIImageView属性为单元格突出显示时设置替代图像。

Figure 5-4 gives an example of a table view whose rows are drawn using a UITableViewCell object in the UITableViewCellStyleSubtitle style; it includes both an image and, for textual content, a title and a subtitle.

图5-4给出了一个表格视图的例子,其中的行使用UITableViewCellStyleSubtitle样式中的UITableViewCell对象绘制; 它包含图像和文字内容,包括标题和副标题。

Figure 5-4  A table view with rows showing both images and text

Listing 5-1 shows the implementation of tableView:cellForRowAtIndexPath: that creates the table view rows in Figure 5-4. The first thing the data source should do is send dequeueReusableCellWithIdentifier: to the table view, passing in a reuse identifier. If a prototype for the cell exists in a storyboard, the table view returns a reusable cell object. Then it sets the cell object’s content, both text and image.

清单5-1显示了tableView:cellForRowAtIndexPath:的实现tableView:cellForRowAtIndexPath:它创建图5-4中的表视图行。 数据源应该做的第一件事是发送dequeueReusableCellWithIdentifier:向表视图中传入重用标识符。 如果单元格的原型存在于故事板中,则表格视图将返回可重用的单元格对象。 然后它设置单元格对象的内容,包括文本和图像。

Listing 5-1 Configuring a UITableViewCell object with both image and text

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    NSDictionary *item = (NSDictionary *)[self.content objectAtIndex:indexPath.row];
    cell.textLabel.text = [item objectForKey:@"mainTitleKey"];
    cell.detailTextLabel.text = [item objectForKey:@"secondaryTitleKey"];
    NSString *path = [[NSBundle mainBundle] pathForResource:[item objectForKey:@"imageKey"] ofType:@"png"];
    UIImage *theImage = [UIImage imageWithContentsOfFile:path];
    cell.imageView.image = theImage;
    return cell;
}

The table view’s data source implementation of tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell.

tableView:cellForRowAtIndexPath:的表视图的数据源实现应该在重用单元时始终重置所有内容。

When you configure a UITableViewCell object, you can also set various other properties, including (but not limited to) the following:

在配置UITableViewCell对象时,还可以设置各种其他属性,包括(但不限于)以下内容:

  • selectionStyle—Controls the appearance of the cell when selected.
  • accessoryType and accessoryView—Allow you to set one of the standard accessory views (disclosure indicator or detail disclosure control) or a custom accessory view for a cell in normal (nonediting) mode. For a custom view, you may provide any UIView object, such as a slider, a switch, or a custom view.
  • editingAccessoryType and editingAccessoryView—Allow you to set one of the standard accessory views (disclosure indicator or detail disclosure control) or a custom accessory view for a cell in editing mode. For a custom view, you may provide any UIView object, such as a slider, a switch, or a custom view.
  • showsReorderControl—Specifies whether it shows a reordering control when in editing mode. The related but read-onlyeditingStyle property specifies the type of editing control the cell has (if any). The delegate returns the value of theeditingStyle property in its implementation of the tableView:editingStyleForRowAtIndexPath: method.
  • backgroundView and selectedBackgroundView—Provide a background view (when a cell is unselected and selected) to display behind all other views of the cell.
  • indentationLevel and indentationWidth—Specify the indentation level for cell content and the width of each indentation level.

  • selectionStyle - selectionStyle控制单元格的外观。

  • accessoryType和accessoryView允许您在正常(非编辑)模式下为单元格设置其中一个标准附件视图(披露指示符或详细信息披露控制)或自定义附件视图。 对于自定义视图,您可以提供任何UIView对象,例如滑块,开关或自定义视图。
  • editingAccessoryType和editingAccessoryView允许您在编辑模式下为单元格设置标准附件视图(披露指标或明细披露控制)或自定义附件视图。 对于自定义视图,您可以提供任何UIView对象,例如滑块,开关或自定义视图。
  • showsReorderControl在编辑模式下是否显示重新排序控件。 相关但只读的editingStyle属性指定单元格所具有的编辑控件的类型(如果有)。 委托在其实现tableView:editingStyleForRowAtIndexPath:方法中返回editingStyle属性的值。
  • backgroundView和selectedBackgroundView - 提供一个背景视图(当一个单元格被取消选定时)显示在单元格的所有其他视图之后。
  • indentationLevel和indentationWidth - 指定单元格内容的缩进级别和每个缩进级别的宽度。

Because a table view cell inherits from UIView, you can also affect its appearance and behavior by setting the properties defined by that superclass. For example, to affect a cell’s background color, you could set its backgroundColor property. Listing 5-2shows how you might use the delegate method tableView:willDisplayCell:forRowAtIndexPath: to alternate the background color of rows (via their backing cells) in a table view.

由于表视图单元格从UIView继承,因此您还可以通过设置由该超类定义的属性来影响其外观和行为。 例如,要影响单元格的背景色,可以设置其backgroundColor属性。 清单5-2显示了如何使用委托方法tableView:willDisplayCell:forRowAtIndexPath:在表视图中交替行(通过它们的支持单元格)的背景颜色。

Listing 5-2 Alternating the background color of cells

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row%2 == 0) {
        UIColor *altCellColor = [UIColor colorWithWhite:0.7 alpha:0.1];
        cell.backgroundColor = altCellColor;
    }
}

Listing 5-2 also illustrates an important aspect of the table view API. A table view sends a tableView:willDisplayCell:forRowAtIndexPath: message to its delegate just before it draws a row. If the delegate chooses to implement this method, it can make last-minute changes to the cell object before it is displayed. With this method, the delegate should change only state-based properties that were set earlier by the table view, such as selection and background color, and not content.

清单5-2还说明了表视图API的一个重要方面。 表视图tableView:willDisplayCell:forRowAtIndexPath:发送一个tableView:willDisplayCell:forRowAtIndexPath:消息 给它的代理, 就在它画一排之前。 如果代理选择实施此方法,则可以在显示单元对象之前进行最后一刻的更改。 使用这种方法,代理应该只更改表视图较早设置的基于状态的属性,例如选择和背景颜色,而不是内容。

Customizing Cells(自定义单元格)

The four predefined styles of UITableViewCell objects suffice for most of the rows that table views display. With these ready-made cell objects, rows can include one or two styles of text, often an image, and an accessory view of some sort. The application can modify the text in its font, color, and other characteristics, and it can supply an image for the row in its selected state as well as its normal state.

四个预定义的UITableViewCell对象样式足以满足表视图显示的大部分行。 使用这些现成的单元格对象,行可以包含一种或两种样式的文本,通常是图像和某种附件视图。 应用程序可以修改文本的字体,颜色和其他特征,并且可以为处于选定状态的行以及其正常状态提供图像。

As flexible and useful as this cell content is, it might not satisfy the requirements of all applications. For example, the labels permitted by a native UITableViewCell object are pinned to specific locations within a row, and the image must appear on the left side of the row. If you want the cell to have different content components and to have these laid out in different locations, or if you want different behavioral characteristics for the cell, you have two alternatives:

由于此单元格内容灵活且有用,因此可能无法满足所有应用程序的要求。 例如,本机UITableViewCell对象所允许的标签被固定到一行内的特定位置,并且该图像必须出现在该行的左侧。 如果您希望单元格具有不同的内容组件并将它们放置在不同的位置,或者如果您希望单元格具有不同的行为特征,则可以有两种选择:

  • Add subviews to a cell’s content view.
  • Create a custom subclass of UITableViewCell.

  • 将子视图添加到单元格的内容视图。

  • 创建一个UITableViewCell的自定义子类。

The following sections discuss both approaches.

以下部分讨论这两种方法。

Loading Table View Cells from a Storyboard(从Storyboard加载表格视图单元格)

In a storyboard, the cells in a table view are dynamic or static. With dynamic content, the table view is a list with a large (and potentially unbounded) number of rows. With static content, the number of rows is a finite quantity that’s known at compile time. A table view that presents a detail view of an item is a good candidate for static content.

在一个 故事板 ,表格视图中的单元格是动态的或静态的。 对于动态内容,表视图是一个包含大量(可能无限制)行数的列表。 对于静态内容,行数是编译时已知的有限数量。 呈现项目详细视图的表格视图是静态内容的很好候选。

You can design dynamic or static cell content directly inside a table view object. Figure 5-5 shows the master and detail table views in a simple storyboard. In this example, the master table view contains dynamic prototype cells, and the detail table view contains static cells.

您可以直接在表格视图对象内设计动态或静态单元格内容。 图5-5显示了一个简单的故事板中的主表和详细表视图。 在此示例中,主表视图包含动态原型单元,并且详细表视图包含静态单元。

Figure 5-5  Table view cells in a storyboard

The following sections demonstrate how to load data into table views that contain custom-configured cells.

以下部分演示如何将数据加载到包含自定义配置的单元格的表视图中。

The Technique for Dynamic Row Content(动态行内容技术)

In this section, you compose a custom prototype cell in a storyboard. At runtime, the data source dequeues cells, prepares them, and gives them to its table view for drawing the rows depicted in Figure 5-6.

在本节中,您将在故事板中创建一个自定义原型单元格。 在运行时, 数据源 使单元出列,准备它们,并将它们提供给它的表视图以绘制图5-6中所示的行。

Figure 5-6  Table view rows drawn with a custom prototype cell

The data source can use two different ways to access the subviews of the cells. One approach uses the tag property defined by UIView and the other approach uses outlets. Using tags is convenient, although it makes the code more fragile because it introduces a coupling between the tag numbers in the storyboard and the code. Using outlets requires a little more work because you need to define a custom subclass of UITableViewCell. Both approaches are described here.

数据源可以使用两种不同的方法来访问单元格的子视图。 一种方法使用UIView定义的tag属性,另一种方法使用outlets。 使用标签很方便,但它使代码更加脆弱,因为它引入了故事板中的标签号和代码之间的耦合。 使用outlets需要更多的工作,因为您需要定义UITableViewCell的自定义子类。 这里介绍两种方法。

To create a project that uses a storyboard to load custom table view cells

创建一个使用故事板加载自定义表格视图单元格的项目

  1. Create a project using the Master-Detail Application template and select the Use Storyboards option.
  2. On the storyboard canvas, select the master view controller.
  3. In the Identity inspector, verify that Class is set to the custom MasterViewController class.
  4. Select the table view inside the master view controller.
  5. In the Attributes inspector, verify that the Content pop-up menu is set to Dynamic Prototypes.
  6. Select the prototype cell.
  7. In the Attributes inspector, choose Custom in the Style pop-up menu.
  8. Enter a reuse identifier in the Identifier text field.
    This is the same reuse identifier you send to the table view in the dequeueReusableCellWithIdentifier: message. For an example, see Listing 5-3.
  9. Choose Disclosure Indicator in the Accessory pop-up menu.
  10. Drag objects from the Library onto the cell.
    For this example, drag two label objects and position them near the ends of the cell (leaving room for the accessory view).
  11. Select the objects and set their attributes, sizes, and autoresizing characteristics.
    An important attribute to set for the programmatic portion of this procedure is each object’s tag property. Find this property in the View section of the Attributes inspector and assign each object a unique integer.

  12. 使用Master-Detail Application模板创建一个项目并选择Use Storyboards选项。

  13. 在故事板画布上,选择主视图控制器。
  14. 在“身份”检查器中,验证“类”是否设置为自定义MasterViewController类。
  15. 选择主视图控制器内的表视图。
  16. 在“属性”检查器中,验证“内容”弹出式菜单是否设置为“动态原型”。
  17. 选择原型单元格。
  18. 在“属性”检查器中,在“样式”弹出式菜单中选择“自定义”。
  19. 在标识符文本字段中输入重用标识符。
    这是您发送给dequeueReusableCellWithIdentifier:消息中的表视图的相同重用标识符。 有关示例,请参见清单5-3 。
  20. 在Accessory弹出式菜单中选择Disclosure Indicator。
  21. 将库中的对象拖到单元格中。
    在这个例子中,拖动两个标签对象并将它们放在单元格末端附近(为附件视图留出空间)。
  22. 选择对象并设置它们的属性,大小和自动修改特征。
    为此过程的编程部分设置的一个重要属性是每个对象的tag属性。 在“属性”检查器的“查看”部分中查找此属性,并为每个对象分配一个唯一的整数。

Now write the code you would normally write to obtain the table view’s data. (For this example, the only data you need is the row number of each cell.) Implement the data source method tableView:cellForRowAtIndexPath: to create a new cell from the prototype and populate it with data, in a manner similar to Listing 5-3.

现在编写您通常会写入的代码以获取表视图的数据。 (在本例中,您需要的唯一数据是每个单元格的行号。)实现数据源方法tableView:cellForRowAtIndexPath:从原型中创建一个新单元格并使用数据填充它,其方式与清单5- 3 。

Listing 5-3 Adding data to a cell using tags

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    UILabel *label;

    label = (UILabel *)[cell viewWithTag:1];
    label.text = [NSString stringWithFormat:@"%d", indexPath.row];

    label = (UILabel *)[cell viewWithTag:2];
    label.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS - indexPath.row];

    return cell;
}

There are a few aspects of this code to note:

这个代码有几个方面需要注意:

  • The string identifier you assign to the prototype cell is the same string you pass to the table view indequeueReusableCellWithIdentifier:.
  • Because the prototype cell is defined in a storyboard, the dequeueReusableCellWithIdentifier: method always returns a valid cell. You don’t need to check the return value against nil and create a cell manually.
  • The code gets the labels in the cell by calling viewWithTag:, passing in their tag integers. It can then set the textual content of the labels.

  • 分配给原型单元格的字符串标识符与传递给dequeueReusableCellWithIdentifier:的表视图的字符串相同dequeueReusableCellWithIdentifier: 。

  • 由于原型单元格是在故事板中定义的,因此dequeueReusableCellWithIdentifier:方法始终返回有效的单元格。 您无需检查返回值nil并手动创建单元格。
  • 代码通过调用viewWithTag:获取单元格中的标签,传递它们的标签整数。 然后它可以设置标签的文本内容。

If you prefer not to use tags, you can use an alternative method for setting the content in the cell. Define a custom UITableViewCell subclass with outlet properties for the objects you want to set. In the storyboard, associate the new class with the prototype cell and connect the outlets to the corresponding objects in the cell.

如果您不想使用标签,则可以使用其他方法设置单元格中的内容。 为要设置的对象定义具有插座属性的自定义UITableViewCell子类。 在故事板中,将新类与原型单元相关联,并将出口连接到单元中的相应对象。

To use outlets for the custom cell content

为自定义单元格内容使用outlets

  • Add an Objective-C class named MyTableViewCell to your project.
  • 将名为MyTableViewCell的Objective-C类添加到您的项目中。
  • Add the following code to the interface in MyTableViewCell.h:
  • 将以下代码添加到MyTableViewCell.h的接口中:
@interface MyTableViewCell : UITableViewCell

@property (nonatomic, weak) IBOutlet UILabel *firstLabel;
@property (nonatomic, weak) IBOutlet UILabel *secondLabel;
@end
  • Add the following code to the implementation in MyTableViewCell.m:
  • 将以下代码添加到MyTableViewCell.m的实现中:
@synthesize firstLabel, secondLabel;
  • Add the following line of code to the source file that implements the data source:
  • 将下面的代码行添加到实现数据源的源文件中:
#import "MyTableViewCell.h"
  • Use the Identity inspector to set the Class of the prototype cell to MyTableViewCell.
  • 使用Identity检查器将原型单元的Class设置为MyTableViewCell 。

  • Use the Connections inspector to connect the two outlets in the prototype cell to their corresponding labels.

  • 使用连接检查器将原型单元中的两个出口连接到相应的标签。

  • Implement the data source method tableView:cellForRowAtIndexPath: in a manner similar to Listing 5-4.
  • 以类似于清单5-4的方式实现数据源方法tableView:cellForRowAtIndexPath:

Listing 5-4 Adding data to a cell using outlets

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    cell.firstLabel.text = [NSString stringWithFormat:@"%d", indexPath.row];
    cell.secondLabel.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS - indexPath.row];

    return cell;
}

The code gains access to the labels in the cell using accessor methods (dot notation is used here). The code can then set the textual content of the labels.

该代码使用访问器方法访问单元格中的标签(此处使用点符号)。 代码然后可以设置标签的文本内容。

The Technique for Static Row Content(静态行内容技术)

In this section, you compose several cells in a table view with static content. At runtime, when the table view is loaded from the storyboard, the table view controller has immediate access to these cells and composes the sections and rows of the table view with them, as depicted in Figure 5-7.

在本节中,您将使用静态内容在表视图中组合多个单元格。 在运行时,当从视图板加载表视图时,表视图控制器可以立即访问这些单元,并与它们一起组成表视图的部分和行, 如图5-7所示 。

Figure 5-7  Table view rows drawn with multiple cells

As with the procedure for dynamic content, start by adding a subclass of UITableViewController to your project. Define outlet properties for the master row label in the first cell and the slider value label in the last cell, as shown in Listing 5-5.

与动态内容的过程一样,首先将UITableViewController的子类添加到项目中。 定义outlet 属性, 对于第一个单元格中的主行标签和最后一个单元格中的滑块值标签,如清单5-5所示。

Listing 5-5 Defining outlet properties for static cell objects

@interface DetailViewController : UITableViewController

@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *masterRowLabel;
@property (weak, nonatomic) IBOutlet UILabel *sliderValueLabel;
@property (weak, nonatomic) IBOutlet UISlider *slider;

- (IBAction)logHello;
- (IBAction)sliderValueChanged:(UISlider *)slider;

@end

In the storyboard, drag a Table View Controller object from the Library onto the canvas. Select the table view and set the following attributes in the Attributes inspector:

在故事板中,将库中的Table View Controller对象拖动到画布上。 选择表格视图并在“属性”检查器中设置以下属性:

  1. Set the Content pop-up menu to Static Cells.
  2. Set the number of sections to 2.
  3. Set the Style pop-up menu to Grouped.

  4. 将内容弹出式菜单设置为静态单元格。

  5. 将部分数设置为2。
  6. 将样式弹出菜单设置为分组。

For each section in the table view, use the Attributes inspector to enter a string in the Header field. Then for the cells, complete the following steps:

对于表格视图中的每个部分,使用“属性”检查器在“标题”字段中输入一个字符串。 然后对于单元格,请完成以下步骤:

  1. Delete two of the three cells in the first table-view section and one cell in the second section.
  2. Increase the height of each remaining cell as needed.
    It isn’t necessary to assign reuse identifiers of these cells, because you’re not going to implement the data source method tableView:cellForRowAtIndexPath:.
  3. Drag objects from the Library to compose the subviews of each cell as depicted in Figure 5-7.
  4. Set any desired attributes of these objects.
    The slider in this example has a range of values from 0 to 10 with an initial value of 7.5.

  5. 删除第一个表视图部分中的三个单元格中的两个和第二部分中的一个单元格。

  6. 根据需要增加每个剩余单元格的高度。
    没有必要为这些单元格指定重用标识符,因为您不打算实现数据源方法tableView:cellForRowAtIndexPath:
  7. 从库中拖动对象来组成每个单元格的子视图, 如图5-7所示 。
  8. 设置任何所需的 属性 这些对象。
    此示例中的滑块的范围值从0到10,初始值为7.5。

Select the table view controller and display the Connections inspector. Make connections between the three outlets in your table view controller and the corresponding objects, as shown in Figure 5-8. While you’re at it, implement the two action methods declared in Listing 5-5 and make target-action connections to the button and the slider.

选择表格视图控制器并显示连接检查器。 在桌面视图控制器的三个插座和相应的对象之间建立连接, 如图5-8所示。 在执行时,请实现清单5-5中声明的两个操作方法,并与按钮和滑块进行目标操作连接。

Figure 5-8  Making connections to your static cell content

To populate the data in the static cells, implement a method called configureView in the detail view controller. In this example, detailItem is an NSString object passed in by the master view controller in its prepareForSegue:sender:method. The string contains the master row number.

要在静态单元格中填充数据,请在详细视图控制器中实施名为configureView的方法。 在此示例中, detailItem是主视图控制器在其prepareForSegue:sender:方法中传入的NSString对象。 该字符串包含主行号。

Listing 5-6 Setting the data in the user interface

- (void)configureView
{
    if (self.detailItem) {
        self.masterRowLabel.text = [self.detailItem description];
    }
    self.sliderValueLabel.text = [NSString stringWithFormat:@"%1.1f", self.slider.value];
}

The detail view controller calls the configureView method in viewDidLoad and setDetailItem:, as illustrated in the Xcode template Master-Detail Application.

详细信息视图控制器在viewDidLoad和setDetailItem:调用configureView方法,如Xcode模板Master-Detail应用程序中所示。

Programmatically Adding Subviews to a Cell’s Content View(以编程方式将子视图添加到单元格的内容视图)

A cell that a table view uses for displaying a row is a view (UITableViewCell inherits from UIView). As a view, a cell has a content view—a superview for cell content—that it exposes as a property. To customize the appearance of rows in a table view, add subviews to the cell’s content view, which is accessible through its contentView property, and lay them out in the desired locations in their superview’s coordinates. You can configure and lay them out programmatically or in Interface Builder. (The approach using Interface Builder is discussed in Loading Table View Cells from a Storyboard.)

表视图用于显示行的单元格是视图( UITableViewCell继承自UIView )。 作为一个视图,一个单元格有一个内容视图 - 一个单元格内容的超级视图 - 它公开为一个单元格 属性。 要自定义表格视图中行的外观,请将子视图添加到可通过其contentView属性访问的单元格内容视图中,并将其置于其超级视图坐标中的所需位置。 您可以通过编程或在Interface Builder中进行配置和布局。 (使用Interface Builder的方法在从故事板加载表格视图单元中讨论。)

One advantage of this approach is its relative simplicity; it doesn’t require you to create a custom subclass of UITableViewCelland handle all of the implementation details required for custom views. However, if you do take this approach, avoid making the views transparent, if you can. Transparent subviews affect scrolling performance because of the increased compositing cost. Subviews should be opaque, and typically should have the same background color as the cell. And if the cell is selectable, make sure that the cell content is highlighted appropriately when selected. The content is selected automatically if the subview implements (if appropriate) the accessor methods for the highlighted property.

这种方法的一个优点是其相对简单; 它不要求您创建UITableViewCell的自定义子类并处理自定义视图所需的所有实现细节。 但是,如果您采取这种方法,请尽量避免使视图透明。由于合成成本增加,透明子视图会影响滚动性能。 子视图应该是不透明的,并且通常应该与单元具有相同的背景颜色。 如果单元格可选,请确保单元格内容在选定时适当突出显示。如果子视图实现(如果适用),则会自动选择内容 访问器方法 为highlighted属性。

Suppose you want a cell with text and image content in custom locations. For example, you want the image on the right side of the cell and the title and subtitle of the cell right-aligned against the left side of the image. Figure 5-9 show how a table view with rows drawn with such a cell might look. (This example is for illustration only, and is not intended as a human-interface model.)

假设您想要一个包含自定义位置文本和图片内容的单元格。 例如,您希望单元格右侧的图像和单元格的标题和字幕与图像左侧对齐。 图5-9显示了如何使用这样的单元格绘制的行的表视图。 (这个例子仅用于说明,并不打算作为人机界面模型。)

Figure 5-9  Cells with custom content as subviews

The code example in Listing 5-7 illustrates how the data source programmatically composes the cell with which this table view draws its rows. In tableView:cellForRowAtIndexPath:, it first checks to see the table view already has a cell object with the given reuse identifier. If there is no such object, the data source creates two label objects and one image view with specific frames within the coordinate system of their superview (the content view). It also sets attributes of these objects. Having acquired an appropriate cell to use, the data source sets the cell’s content before returning the cell.

清单5-7中的代码示例说明了 ,数据源 以编程方式编写此表视图绘制其行的单元格。 在tableView:cellForRowAtIndexPath: ,它首先检查以查看表视图已经具有给定重用标识符的单元对象。 如果没有这样的对象,则数据源将在其超级视图的坐标系(内容视图)内创建两个标签对象和一个带有特定框架的图像视图。 它还设置这些对象的属性。 在获得了适当的单元格后,数据源会在返回单元格之前设置单元格的内容。

Listing 5-7 Adding subviews to a cell’s content view

#define MAINLABEL_TAG 1
#define SECONDLABEL_TAG 2
#define PHOTO_TAG 3

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"ImageOnRightCell";

    UILabel *mainLabel, *secondLabel;
    UIImageView *photo;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;

        mainLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 220.0, 15.0)];
        mainLabel.tag = MAINLABEL_TAG;
        mainLabel.font = [UIFont systemFontOfSize:14.0];
        mainLabel.textAlignment = UITextAlignmentRight;
        mainLabel.textColor = [UIColor blackColor];
        mainLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:mainLabel];

        secondLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 20.0, 220.0, 25.0)];
        secondLabel.tag = SECONDLABEL_TAG;
        secondLabel.font = [UIFont systemFontOfSize:12.0];
        secondLabel.textAlignment = UITextAlignmentRight;
        secondLabel.textColor = [UIColor darkGrayColor];
        secondLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:secondLabel];

        photo = [[UIImageView alloc] initWithFrame:CGRectMake(225.0, 0.0, 80.0, 45.0)];
        photo.tag = PHOTO_TAG;
        photo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:photo];
    } else {
        mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
        secondLabel = (UILabel *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
        photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
    }
    NSDictionary *aDict = [self.list objectAtIndex:indexPath.row];
    mainLabel.text = [aDict objectForKey:@"mainTitleKey"];
    secondLabel.text = [aDict objectForKey:@"secondaryTitleKey"];
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:[aDict objectForKey:@"imageKey"] ofType:@"png"];
    UIImage *theImage = [UIImage imageWithContentsOfFile:imagePath];
    photo.image = theImage;

    return cell;
}

When the data source creates the cells, it assigns each subview an identifier called a tag. With tags, you can locate a view in its view hierarchy by calling the viewWithTag: method. If the delegate later gets the designated cell from the table view’s queue, it uses the tags to obtain references to the three subviews prior to assigning them content.

数据源创建单元格时,会为每个子视图分配一个称为标记的标识符。 使用标签,您可以通过调用viewWithTag:方法在其视图层次结构中查找视图。 如果 代理 稍后从表视图的队列中获取指定的单元格,它在分配内容之前使用标记获取对三个子视图的引用。

This code creates a UITableViewCell object in the predefined default style (UITableViewCellStyleDefault). Because the content properties of the standard cells—textLabel, detailTextLabel, and imageView—are nil until assigned content, you may use any predefined cell as the template for customization.

此代码以预定义的默认样式( UITableViewCellStyleDefault )创建一个UITableViewCell对象。 由于标准单元格textLabel , detailTextLabel和textLabel的内容属性在分配内容前一直nil ,因此您可以使用任何预定义的单元格作为自定义模板。

Note: Another approach is to subclass UITableViewCell and create instances in the UITableViewCellStyleSubtitlestyle. Then override the layoutSubviews method to reposition the textLabel, detailTextLabel, and imageViewsubviews (after calling super).

注意:另一种方法是对UITableViewCell进行子类化并使用UITableViewCellStyleSubtitle样式创建实例。 然后重写layoutSubviews方法以重新定位textLabel , detailTextLabel和detailTextLabel子视图(调用super )。

One way to achieve “attributed string” effects with textual content is to lay out UILabel subviews of the UITableViewCellcontent view. The text of each label can have its own font, color, size, alignment, and other characteristics. If you want that kind of variation within a label object, create multiple labels and lay them out relative to each other.

用文本内容实现“属性字符串”效果的一种方法是布置UITableViewCell内容视图的UILabel子视图。 每个标签的文本可以有自己的字体,颜色,大小,对齐方式和其他特征。 如果你想要在标签对象中进行这种变化,可以创建多个标签并将它们相对放置。

Enhancing the Accessibility of Table View Cells(增强表格视图单元格的可访问性)

If your app displays a table view in which each cell contains items other than (or in addition to) text, there are a few things you can do to make it more accessible. Similarly, if your table view displays more than one piece of information per row, you can enhance a VoiceOver user’s experience by aggregating the information in a single, easy-to-understand label.

如果您的应用程序显示的表格视图中的每个单元格都包含文本以外的其他项目(或除文本外),则可以执行一些操作以使其更加容易 无障碍 。 同样,如果您的表格视图在每行中显示多条信息,则可以通过将信息汇总到一个易于理解的标签中来增强VoiceOver用户的体验。

Note: If your table cells contain any of the standard table-view elements, such as the disclosure indicator, detail disclosure button, or delete control, you do not have to do anything to make these elements accessible. If, however, your table cells include other types of controls, such as a switch or a slider, you need to make sure that these elements are appropriately accessible.

注意:如果您的表格单元格包含任何标准的表格视图元素,例如披露指示符,详细信息披露按钮或删除控件,则无需执行任何操作即可使这些元素可访问。 但是,如果表格单元格包含其他类型的控件(如开关或滑块),则需要确保这些元素可以正确访问。

If the table cells in your app contain a mix of different elements, determine whether users interact with each cell as a unit, or with individual elements inside the cell. If users need to access individual elements inside the cell, you should:

如果您的应用中的表格单元格包含不同元素的混合,请确定用户是以每个单元格作为单元进行交互,还是使用单元格内的单个元素进行交互。 如果用户需要访问单元内的单个元素,则应该:

  • Make each individual element accessible separately.
  • Make sure the table cell itself is not accessible.
  • Succinctly describe the overall contents of the cell and use this description for the label attribute of the cell. Note that, in this case, the label is considered to be one of the accessible elements within the cell.

  • 使每个单独的元素可以单独访问

  • 确保表格单元本身不可访问。
  • 简要描述单元的整体内容,并将此描述用于单元的标签属性。 请注意,在这种情况下,标签被认为是单元格内的可访问元素之一。

You’ve probably recognized that a table cell that contains multiple items, such as text and controls, fits the criteria of a container view, as defined by the UI Accessibility programming interface. However, you do not have to identify the cell as a container view or implement any of the methods of the UIAccessibilityContainer protocol, because the table cell is automatically designated as a container.

您可能已经认识到,包含多个项目(如文本和控件)的表格单元格符合容器视图的条件,如UI辅助功能编程界面所定义的。 但是,您不必将单元标识为容器视图,也不必实现UIAccessibilityContainer协议的任何方法,因为表格单元自动指定为容器。

If your table contains cells that provide information in discrete chunks, you should consider combining the information from these chunks in the label attribute. When you do this, VoiceOver users can get the meaning of the cell’s contents with one gesture, instead of having to access each piece of information separately.

如果您的表格包含以离散块提供信息的单元格,则应考虑将这些块中的信息组合到标签属性中。 当您这样做时,VoiceOver用户可以用一个手势来获取单元格内容的含义,而不必分别访问每条信息。

A good example of how this can work is in the built-in Stocks app. Instead of providing the company name, current stock price, and change in price as separate strings, Stocks combines this information in the label, which might sound like this: “Apple Inc., $648.11, up 1.85%.” Notice the commas in this label. When you combine discrete pieces of information in this way, you can use commas to tell VoiceOver to pause briefly between phrases, making it easier for users to understand the information.

一个很好的例子就是内置的股票应用程序。 股票并没有提供公司名称,当前股票价格和价格变化作为单独的字符串,而是将这些信息结合在标签中,这听起来可能是这样的:“苹果公司,648.11美元,上涨1.85%。”注意这里的逗号标签,当您以这种方式组合不连续的信息时,可以使用逗号来指示VoiceOver在短语之间短暂暂停,从而使用户更容易理解信息。

Listing 5-8 shows how to combine the information in the labels of two separate elements into a single label that describes both:

清单5-8显示了如何将两个独立元素的标签中的信息合并到一个标签中,该标签描述了两个标签:

Listing 5-8 Concatenating labels of a table cell

@implementation WeatherTableViewController
// This is a view that provides weather information. It contains a city subview and a temperature subview, each of which provides a separate label.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    // set up the cell here...

    NSString *cityLabel = [self.weatherCity accessibilityLabel];
    NSString *temperatureLabel = [self.weatherTemp accessibilityLabel];

    // Combine the city and temperature information so that VoiceOver users can get the weather information with one gesture.
    [cell setAccessibilityLabel:[NSString stringWithFormat:@"%@, %@", cityLabel, temperatureLabel]];
    return cell;
}
@end

Joining the table cell’s accessibility labels isn’t the only thing you can do to enhance the overall accessibility of your table view. You can also change the way VoiceOver reads the table view’s indexed list. To learn more, read Populating an Indexed List.

加入表格单元格的辅助功能标签并不是唯一可以增强表格视图的整体可访问性的方法。 您还可以更改VoiceOver读取表格视图索引列表的方式。 要了解更多信息,请阅读填充索引列表 。

Cells and Table View Performance(单元格和表格视图性能)

The proper use of table view cells, whether off-the-shelf or custom cell objects, is a major factor in the performance of table views. Ensure that your application does the following three things:

正确使用表格视图单元格,不管是现成的还是自定义的单元格对象,都是表格视图性能的主要因素。 确保您的应用程序执行以下三件事情:

  • Reuse cells. Object allocation has a performance cost, especially if the allocation has to happen repeatedly over a short period—say, when the user scrolls a table view. If you reuse cells instead of allocating new ones, you greatly enhance table view performance.
  • Avoid relayout of content. When reusing cells with custom subviews, refrain from laying out those subviews each time the table view requests a cell. Lay out the subviews once, when the cell is created.
  • Use opaque subviews. When customizing table view cells, make the subviews of the cell opaque, not transparent.

  • 重用单元格 。对象分配具有性能成本,特别是如果分配必须在短时间内重复发生 - 例如用户滚动表视图时。如果您重复使用单元格而不是分配新单元格,则会大大增强表格视图的性能。

  • 避免重新发布内容。当重复使用具有自定义子视图的单元格时,请避免在每次表视图请求单元格时布置这些子视图。在创建单元格时,将子视图布置一次。
  • 使用不透明的子视图。定制表格视图单元格时,使单元格的子视图不透明,不透明。

Managing Selections(管理选择)

When users tap a row of a table view, usually something happens as a result. Another table view could slide into place, the row could display a checkmark, or some other action could be performed. The following sections describe how to respond to selections and how to make selections programmatically.

当用户点击一行表格视图时,通常会发生一些结果。 另一个表格视图可以滑动到位,该行可以显示复选标记,或者可以执行一些其他操作。 以下各节介绍如何响应选择以及如何以编程方式进行选择。

Selections in Table Views(表视图中的选择)

There are a few human-interface guidelines to keep in mind when dealing with cell selection in table views:

在处理表格视图中的单元格选择时,需要记住一些人机界面准则:

  • You should never use selection to indicate state. Instead, use check marks and accessory views for showing state.
  • When the user selects a cell, you should respond by deselecting the previously selected cell (by calling thedeselectRowAtIndexPath:animated: method) as well as by performing any appropriate action, such as displaying a detail view.
  • If you respond to the the selection of a cell by pushing a new view controller onto the navigation controller’s stack, you should deselect the cell (with animation) when the view controller is popped off the stack.

  • 你不应该使用选择来表示状态。 相反,请使用复选标记和附件视图来显示状态。

  • 当用户选择一个单元格时,您应该通过取消选择先前选择的单元格(通过调用deselectRowAtIndexPath:animated:方法)以及执行任何适当的操作(例如显示详细视图)来做出响应。
  • 如果您通过推送新的单元来响应单元格的选择 视图控制器 在导航控制器的堆栈上,当视图控制器从堆栈弹出时,您应该取消选择该单元(带有动画)。

You can control whether rows are selectable when the table view is in editing mode by setting the allowsSelectionDuringEditing property of UITableView. In addition, beginning with iOS 3.0, you can control whether cells are selectable when editing mode is not in effect by setting the allowsSelection property.

通过设置UITableView的allowsSelectionDuringEditing属性,您可以控制在表视图处于编辑模式时行是否可选。 另外,从iOS 3.0开始,您可以通过设置allowsSelection属性来控制在编辑模式无效时是否可以选择单元格。

Responding to Selections(响应选择)

Users tap a row in a table view either to signal to the application that they want to know more about what that row signifies or to select what the row represents. In response to the user tapping a row, an application could do any of the following:

用户在表格视图中点击一行,向应用程序表明他们想知道更多关于该行所代表的内容,或者选择该行所代表的内容。 为了响应用户点击一行,应用程序可以执行以下任何操作:

  • Show the next level in a data-model hierarchy.
  • Show a detail view of an item (that is, a leaf node of the data-model hierarchy).
  • Show a checkmark in the row to indicate that the represented item is selected.
  • If the touch occurred in a control embedded in the row, it could respond to the action message sent by the control.

  • 在数据模型层次结构中显示下一个级别。

  • 显示项目的详细视图(即数据模型层次结构的叶节点)。
  • 在行中显示复选标记以指示所选项目已被选中。
  • 如果触摸发生在嵌入行中的控件中,它可以响应控件发送的操作消息。

To handle most selections of rows, the table view’s delegate must implement the tableView:didSelectRowAtIndexPath:method. In sample method implementation shown in Listing 6-1, the delegate first deselects the selected row. Then it allocates and initializes an instance of the next table-view controller in the sequence. It sets the data this view controller needs to populate its table view and then pushes this object onto the stack maintained by the application’s UINavigationControllerobject.

要处理大多数行的选择,表视图的委托必须实现tableView:didSelectRowAtIndexPath:方法。 在清单6-1所示的示例方法实现中, 代理 首先取消选择所选行。 然后呢 分配和初始化 序列中下一个表视图控制器的实例。 它设置此视图控制器填充其表视图所需的数据,然后将此对象压入由应用程序的UINavigationController对象维护的堆栈中。

Listing 6-1 Responding to a row selection

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 {
     [tableView deselectRowAtIndexPath:indexPath animated:NO];
     BATTrailsViewController *trailsController = [[BATTrailsViewController alloc] initWithStyle:UITableViewStylePlain];
     trailsController.selectedRegion = [regions objectAtIndex:indexPath.row];
     [[self navigationController] pushViewController:trailsController animated:YES];
}

If a row has a disclosure control—the white chevron over a blue circle—for an accessory view, clicking the control results in the delegate receiving a tableView:accessoryButtonTappedForRowWithIndexPath: message (instead of tableView:didSelectRowAtIndexPath:). The delegate responds to this message in the same general way as it does for other kinds of selections.

如果一行具有披露控制 - 蓝色圆圈上的白色人字形 - 对于辅助视图,单击该控件会导致接收tableView:accessoryButtonTappedForRowWithIndexPath: message(而不是tableView:didSelectRowAtIndexPath:的委托。 与其他类型的选择一样,代表对此消息的响应方式与通用方式相同。

A row can also have a control object as its accessory view, such as a switch or a slider. This control object functions as it would in any other context: Manipulating the object in the proper way results in an action message being sent to a target object. Listing 6-2 illustrates a data source object that adds a UISwitch object as a cell’s accessory view and then responds to the action messages sent when the switch is “flipped.”

一行也可以有一个控制对象作为其附件视图,例如开关或滑块。 此控件对象的功能与其他任何上下文中的功能相同:以适当的方式操作对象会导致将操作消息发送到目标对象。 清单6-2说明了一个 数据源 对象,它添加一个UISwitch对象作为单元的附件视图,然后响应交换机“翻转”时发送的动作消息。

Listing 6-2 Setting a switch object as an accessory view and responding to its action message

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
       UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"CellWithSwitch"];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellWithSwitch"];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            cell.textLabel.font = [UIFont systemFontOfSize:14];
        }
        UISwitch *switchObj = [[UISwitch alloc] initWithFrame:CGRectMake(1.0, 1.0, 20.0, 20.0)];
        switchObj.on = YES;
        [switchObj addTarget:self action:@selector(toggleSoundEffects:) forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
        cell.accessoryView = switchObj;

        cell.textLabel.text = @"Sound Effects";
        return cell;
}

- (void)toggleSoundEffects:(id)sender {
    [self.soundEffectsOn = [(UISwitch *)sender isOn];
    [self reset];
}

You may also define controls as accessory views of table-view cells created in Interface Builder. Drag a control object (switch, slider, and so on) into a nib document window containing a table-view cell. Then, using the connection window, make the control the accessory view of the cell. Loading Table View Cells from a Storyboard describes the procedure for creating and configuring table-view cell objects in nib files.

您还可以将控件定义为在Interface Builder中创建的表视单元的附件视图。 将控件对象(开关,滑块等)拖到一个 笔尖文档窗口 包含一个表格视图单元格。 然后,使用连接窗口,使控件成为单元的附件视图。 加载表格视图Storyboard中的单元格描述了在nib文件中创建和配置表格单元格对象的过程。

Selection management is also important with selection lists. There are two kinds of selection lists:

选择管理对于选择列表也很重要。 有两种选择列表:

  • Exclusive lists where only one row is permitted the checkmark
  • Inclusive lists where more than one row can have a checkmark

  • 独占列表,其中只有一行是允许的复选标记

  • 包含列表,其中多行可以有复选标记

Listing 6-3 illustrates one approach to managing an exclusive selection list. It first deselects the currently selected row and returns if the same row is selected; otherwise it sets the checkmark accessory type on the newly selected row and removes the checkmark on the previously selected row

清单6-3说明了管理排他选择列表的一种方法。 它首先取消选择当前选中的行,并在选择相同的行时返回; 否则它在新选择的行上设置复选标记附件类型,并删除先前选择的行上的复选标记

Listing 6-3 Managing a selection list—exclusive list

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    NSInteger catIndex = [taskCategories indexOfObject:self.currentCategory];
    if (catIndex == indexPath.row) {
        return;
    }
    NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:catIndex inSection:0];

    UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
    if (newCell.accessoryType == UITableViewCellAccessoryNone) {
        newCell.accessoryType = UITableViewCellAccessoryCheckmark;
        self.currentCategory = [taskCategories objectAtIndex:indexPath.row];
    }

    UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
    if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark) {
        oldCell.accessoryType = UITableViewCellAccessoryNone;
    }
}

Listing 6-4 illustrates how to manage a inclusive selection list. As the comments in this example indicate, when the delegate adds a checkmark to a row or removes one, it typically also sets or unsets any associated model-object attribute.

清单6-4说明了如何管理包含选择列表。 正如本例中的注释所表明的,当委托为行添加复选标记或删除复选标记时,它通常也会设置或取消设置任何关联的模型对象属性。

Listing 6-4 Managing a selection list—inclusive list

- (void)tableView:(UITableView *)theTableView
          didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {

    [theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
    UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
    if (cell.accessoryType == UITableViewCellAccessoryNone) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        // Reflect selection in data model
    } else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
        cell.accessoryType = UITableViewCellAccessoryNone;
        // Reflect deselection in data model
    }
}

In tableView:didSelectRowAtIndexPath: you should always deselect the currently selected row.

在tableView:didSelectRowAtIndexPath:您应该始终取消选择当前选定的行。

Programmatically Selecting and Scrolling(以编程方式选择和滚动)

Occasionally the selection of a row originates within the application itself rather than from a tap in a table view. There could be an externally induced change in the data model. For example, the user adds a new person to an address book and then returns to the list of contacts; the application wants to scroll this list to the recently added person. For situations like these, you can use the UITableView methods selectRowAtIndexPath:animated:scrollPosition: and (if the row is already selected) scrollToNearestSelectedRowAtScrollPosition:animated:. You may also call scrollToRowAtIndexPath:atScrollPosition:animated: if you want to scroll to a specific row without selecting it.

有时候,行的选择始于应用程序本身,而不是从表格视图中点击。 数据模型中可能存在外部诱发的变化。 例如,用户将新人添加到地址簿,然后返回到联系人列表; 应用程序想要将此列表滚动到最近添加的人员。 对于这样的情况,可以使用UITableView方法selectRowAtIndexPath:animated:scrollPosition:和(如果行已被选中) scrollToNearestSelectedRowAtScrollPosition:animated: selectRowAtIndexPath:animated:scrollPosition: 你也可以调用scrollToRowAtIndexPath:atScrollPosition:animated:如果你想滚动到一个特定的行而不选择它。

The code in Listing 6-5 (somewhat whimsically) programmatically selects and scrolls to a row 20 rows away from the just-selected row using the selectRowAtIndexPath:animated:scrollPosition: method.

清单6-5中的代码(有些异想天开)使用selectRowAtIndexPath:animated:scrollPosition:方法以编程方式选择并滚动到距刚刚选择的行20行的行。

Listing 6-5 Programmatically selecting a row

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
    NSIndexPath *scrollIndexPath;
    if (newIndexPath.row + 20 < [timeZoneNames count]) {
        scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row+20 inSection:newIndexPath.section];
    } else {
        scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row-20 inSection:newIndexPath.section];
    }
    [theTableView selectRowAtIndexPath:scrollIndexPath animated:YES
                        scrollPosition:UITableViewScrollPositionMiddle];
}

Inserting and Deleting Rows and Sections(插入和删除行和部分)

A table view has an editing mode as well as its normal (selection) mode. When a table view goes into editing mode, it displays the editing and reordering controls associated with its rows. The editing controls, which are in the left side of the row, allow the user to insert and delete rows in the table view. The editing controls have distinctive appearances:

表视图具有编辑模式以及其正常(选择)模式。 当表格视图进入编辑模式时,它将显示与其行相关的编辑和重新排序控件。 位于该行左侧的编辑控件允许用户在表格视图中插入和删除行。 编辑控件具有独特的外观:

Deletion control

Deletion control

Insertion control

Insertion control

When a table view enters editing mode and when users click an editing control, the table view sends a series of messages to its data source and delegate, but only if they implement these methods. These methods allow the data source and delegate to refine the appearance and behavior of rows in the table view; the messages also enable them to carry out the deletion or insertion operation.

当表视图进入编辑模式并且用户单击编辑控件时,表视图向其发送一系列消息 数据源和代理 ,但前提是他们实施这些方法。 这些方法允许数据源和代理来优化表视图中行的外观和行为; 这些消息还使他们能够执行删除或插入操作。

Even if a table view is not in editing mode, you can insert or delete a number of rows or sections as a group and have those operations animated.

即使表格视图不处于编辑模式,也可以将多个行或部分作为一组插入或删除,并使这些操作成为动画。

The first section below shows you how, when a table is in editing mode, to insert new rows and delete existing rows in a table view in response to user actions. The second section, Batch Insertion, Deletion, and Reloading of Rows and Sections, discusses how you can insert and delete multiple sections and rows animated as a group.

下面的第一部分向您展示了当表处于编辑模式时,如何响应用户操作插入新行并删除表视图中的现有行。 第二部分“ 批量插入,删除和重新加载行和部分 ”讨论了如何插入和删除动画群组中的多个部分和行。

Note: The procedure for reordering rows when in editing mode is described in Managing the Reordering of Rows.

注意:在管理行的重新排序中描述了在编辑模式下重新排序行的过程 。

Inserting and Deleting Rows in Editing Mode(在编辑模式下插入和删除行)

When a Table View is Edited(当编辑表格视图时)

A table view goes into editing mode when it receives a setEditing:animated: message. Typically (but not necessarily) the message originates as an action message sent when the user taps an Edit button in the navigation bar. In editing mode, a table view displays any editing (and reordering) controls that its delegate has assigned to each row. The delegate assigns the controls as a result of returning the editing style for a row in the tableView:editingStyleForRowAtIndexPath: method.

表格视图在接收到setEditing:animated:消息时进入编辑模式。 通常(但不一定)该消息起源于当用户点击导航栏中的编辑按钮时发送的操作消息。 在编辑模式下,表格视图显示任何编辑(和重新排序)的控件 代表 已分配给每一行。 由于返回tableView:editingStyleForRowAtIndexPath:方法中的行的编辑样式,委托会分配控件。

Note: If a UIViewController object is managing the table view, it automatically receives a setEditing:animated:message when the Edit button is tapped. In its implementation of this message, it can update button state or do any other kind of task before invoking the table view’s version of the method.

注意:如果一个UIViewController对象管理着表格视图,当点击Edit按钮时它会自动收到一个setEditing:animated:消息。 在执行此消息时,它可以在调用表视图的方法版本之前更新按钮状态或执行任何其他类型的任务。

When the table view receives setEditing:animated:, it sends the same message to the UITableViewCell object for each visible row. Then it sends a succession of messages to its data source and its delegate (if they implement the methods) as depicted in the diagram in Figure 7-1.

当表视图接收到setEditing:animated: ,它将相同的消息发送给每个可见行的UITableViewCell对象。 然后它将一连串的消息发送到它的数据源及其委托(如果他们实现了这些方法), 如图7-1所示 。

Figure 7-1  Calling sequence for inserting or deleting rows in a table view

After resending setEditing:animated: to the cells corresponding to the visible rows, the sequence of messages is as follows:

重新发送setEditing:animated:到与可见行相对应的单元格后,消息序列如下所示:

  1. The table view invokes the tableView:canEditRowAtIndexPath: method if its data source implements it. This method allows the application to exclude rows in the table view from being edited even when their cell’s editingStyle property indicates otherwise. Most applications do not need to implement this method.
  2. The table view invokes the tableView:editingStyleForRowAtIndexPath: method if its delegate implements it. This method allows the application to specify a row’s editing style and thus the editing control that the row displays.
    At this point, the table view is fully in editing mode. It displays the insertion or deletion control for each eligible row.
  3. The user taps an editing control (either the deletion control or the insertion control). If he or she taps a deletion control, a Delete button is displayed on the row. The user then taps that button to confirm the deletion.
  4. The table view sends the tableView:commitEditingStyle:forRowAtIndexPath: message to the data source. Although this protocol method is marked as optional, the data source must implement it if it wants to insert or delete a row. It must do two things:

    • Send deleteRowsAtIndexPaths:withRowAnimation: or insertRowsAtIndexPaths:withRowAnimation: to the table view to direct it to adjust its presentation.
    • Update the corresponding data-model array by either deleting the referenced item from the array or adding an item to the array.
  5. 如果表视图的数据源实现它,则调用tableView:canEditRowAtIndexPath:方法。 此方法允许应用程序排除表格视图中的行,即使其单元格的editingStyle属性指示了其他行。 大多数应用程序不需要实现这种方法。

  6. 如果表视图的委托实现它,则调用tableView:editingStyleForRowAtIndexPath:方法。 此方法允许应用程序指定行的编辑样式,从而指定行显示的编辑控件。
    此时,表格视图完全处于编辑模式。 它显示每个合格行的插入或删除控制。
  7. 用户点击编辑控件(删除控件或插入控件)。 如果他或她点击删除控制,则在该行上显示删除按钮。 用户然后点击该按钮以确认删除。
  8. 表视图将tableView:commitEditingStyle:forRowAtIndexPath:消息发送到数据源。 虽然这样 协议 方法被标记为可选,数据源必须实现它,如果它想要插入或删除一行。 它必须做两件事:
    • 发送deleteRowsAtIndexPaths:withRowAnimation:或insertRowsAtIndexPaths:withRowAnimation:到表格视图以指导它调整其表示。
    • 通过从数组中删除引用的项目或将项目添加到数组来更新相应的数据模型数组。

When the user swipes across a row to display the Delete button for that row, there is a variation in the calling sequence diagrammed in Figure 7-1. When the user swipes a row to delete it, the table view first checks to see if its data source has implemented the tableView:commitEditingStyle:forRowAtIndexPath: method; if that is so, it sends setEditing:animated: to itself and enters editing mode. In this “swipe to delete” mode, the table view does not display the editing and reordering controls. Because this is a user-driven event, it also brackets the messages to the delegate inside of two other messages: tableView:willBeginEditingRowAtIndexPath: and tableView:didEndEditingRowAtIndexPath:. By implementing these methods, the delegate can update the appearance of the table view appropriately.

当用户在一行上滑动以显示该行的删除按钮时, 图7-1中显示的调用序列有所不同。 当用户划动一行来删除它时,表视图首先检查它的数据源是否实现了tableView:commitEditingStyle:forRowAtIndexPath: method; 如果是这样,它会发送setEditing:animated:到自己并进入编辑模式。 在此“滑动删除”模式下,表格视图不显示编辑和重新排序控件。 因为这是一个用户驱动的事件,所以它还将消息包含在两个其他消息中的委托消息中: tableView:willBeginEditingRowAtIndexPath:和tableView:didEndEditingRowAtIndexPath: 通过实现这些方法,委托可以适当地更新表视图的外观。

Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method.

注意:数据源不应该在tableView:commitEditingStyle:forRowAtIndexPath:实现中调用setEditing:animated:tableView:commitEditingStyle:forRowAtIndexPath: 如果由于某种原因,它必须在延迟之后通过使用performSelector:withObject:afterDelay:方法来调用它。

Although you can use an insertion control as the trigger to insert a new row in a table view, an alternative approach is to have an Add (or plus sign) button in the navigation bar. Tapping the button sends an action message to the view controller, which overlays the table view with a modal view for entering the new item. Once the item is entered, the controller adds it to the data-model array and reloads the table. An Example of Adding a Table-View Row discusses this approach.

虽然可以使用插入控件作为触发器在表视图中插入新行,但另一种方法是在导航栏中添加一个“添加”(或加号)按钮。 点击按钮会向视图控制器发送操作消息,视图控制器会使用模态视图覆盖表视图以输入新项目。 一旦项目被输入,控制器将其添加到数据模型数组并重新加载表格。 添加表视图行的示例讨论了这种方法。

An Example of Deleting a Table-View Row(删除表格视图行的示例)

This section gives a guided tour through the parts of a project that work together to set up a table view for editing mode and delete rows from it. This project uses the navigation controller and view controller architecture to manage its table views. In its loadView method, the custom view controller creates the table view and sets itself to be the data source and delegate. It also sets the right item of the navigation bar to be the standard Edit button.

本节介绍了一个项目的各个部分的导览,这些部分一起工作以设置编辑模式的表格视图并从中删除行。 该项目使用导航控制器和 视图控制器 架构来管理其表格视图。 在其loadView方法中,自定义视图控制器创建表视图并将其自身设置为 数据源和委托 。 它还将导航栏的正确项目设置为标准编辑按钮。

self.navigationItem.rightBarButtonItem = self.editButtonItem;

This button is preconfigured to send setEditing:animated: to the view controller when tapped; it toggles the button title (between Edit and Done) and the Boolean editing parameter on alternating taps. In its implementation of the method, as shown in Listing 7-1, the view controller invokes the superclass invocation of the method, sends the same message to the table view, and updates the enabled state of the other button in the navigation bar (a plus-sign button, for adding items).

此按钮已预先配置为在点击时向视图控制器发送setEditing:animated: 它会切换按钮标题(在编辑和完成之间)和布尔编辑参数交替点击。 在它的实现方法中,如清单7-1所示,视图控制器调用方法的超类调用,将相同的消息发送到表视图,并更新导航栏中另一个按钮的启用状态(一个加号按钮,用于添加项目)。

Listing 7-1 View controller responding to setEditing:animated:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];
    [tableView setEditing:editing animated:YES];
    if (editing) {
        addButton.enabled = NO;
    } else {
        addButton.enabled = YES;
    }
}

When its table view enters editing mode, the view controller specifies a deletion control for every row except the last, which has an insertion control. It does this in its implementation of the tableView:editingStyleForRowAtIndexPath: method (Listing 7-2).

当其表格视图进入编辑模式时,视图控制器为除了最后一个具有插入控制的每一行指定一个删除控制。 它在执行tableView:editingStyleForRowAtIndexPath:方法时执行了这个操作(代码清单7-2 )。

Listing 7-2 Customizing the editing style of rows

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    SimpleEditableListAppDelegate *controller = (SimpleEditableListAppDelegate *)[[UIApplication sharedApplication] delegate];
    if (indexPath.row == [controller countOfList]-1) {
        return UITableViewCellEditingStyleInsert;
    } else {
        return UITableViewCellEditingStyleDelete;
    }
}

The user taps the deletion control in a row and the view controller receives a tableView:commitEditingStyle:forRowAtIndexPath: message from the table view. As shown in Listing 7-3, it handles this message by removing the item corresponding to the row from a model array and sending deleteRowsAtIndexPaths:withRowAnimation: to the table view.

用户轻敲一行中的删除控件,视图控制器从表视图中接收tableView:commitEditingStyle:forRowAtIndexPath:消息。 如清单7-3所示,它通过从模型数组中删除与该行相对应的项并发送deleteRowsAtIndexPaths:withRowAnimation:到表视图来处理此消息。

Listing 7-3 Updating the data-model array and deleting the row

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    // If row is deleted, remove it from the list.
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        SimpleEditableListAppDelegate *controller = (SimpleEditableListAppDelegate *)[[UIApplication sharedApplication] delegate];
        [controller removeObjectFromListAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}

An Example of Adding a Table-View Row(添加表格视图行的示例)

This section shows project code that inserts a row in a table view. Instead of using the insertion control as the trigger for inserting a row, it uses an Add button (visually a plus sign) in the navigation bar above the table view. This code also is based on the navigation controller and view controller architecture. In its loadView method implementation, the view controller assigns the Add button as the right-side item of the navigation bar using the code shown in Listing 7-4.

本部分显示在表格视图中插入行的项目代码。 不是使用插入控件作为插入行的触发器,而是使用表视图上方导航栏中的添加按钮(可视为加号)。 该代码还基于导航控制器和视图控制器体系结构。 在其loadView方法实现中,视图控制器使用清单7-4中显示的代码将Add按钮指定为导航栏的右侧项目。

Listing 7-4 Adding an Add button to the navigation bar

    addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addItem:)];
    self.navigationItem.rightBarButtonItem = addButton;

Note that the view controller sets the control states for the title as well as the action selector and the target object. When the user taps the Add button, the addItem: message is sent to the target (the view controller). It responds as shown in Listing 7-5. It creates a navigation controller with a single view controller whose view is put onscreen modally—it animates upward to overlay the table view. The presentModalViewController:animated: method to do this.

请注意,视图控制器设置标题的控制状态以及操作 选择 和目标对象。 当用户点击添加按钮时, addItem:消息被发送到目标(视图控制器)。 它的响应如清单7-5所示。 它创建一个带有单个视图控制器的导航控制器,其视图以模态方式放置在屏幕上 - 它向上动画以覆盖表视图。 presentModalViewController:animated:方法来执行此操作。

Listing 7-5 Responding to a tap on the Add button

- (void)addItem:sender {
    if (itemInputController == nil) {
        itemInputController = [[ItemInputController alloc] init];
    }
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:itemInputController];
    [[self navigationController] presentModalViewController:navigationController animated:YES];
}

The modal, or overlay, view consists of a single custom text field. The user enters text for the new table-view item and then taps a Save button. This button sends a save: action message to its target: the view controller for the modal view. As shown in Listing 7-6, the view controller extracts the string value from the text field and updates the application’s data-model array with it.

模态或叠加视图由单个自定义文本字段组成。 用户输入新的表格视图项目的文本,然后点击保存按钮。 该按钮发送save: action消息到其目标:模态视图的视图控制器。 如清单7-6所示,视图控制器从文本字段中提取字符串值,并使用它更新应用程序的数据模型数组。

Listing 7-6 Adding the new item to the data-model array

- (void)save:sender {

    UITextField *textField = [(EditableTableViewTextField *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] textField];

    SimpleEditableListAppDelegate *controller = (SimpleEditableListAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSString *newItem = textField.text;
    if (newItem != nil) {
            [controller insertObject:newItem inListAtIndex:[controller countOfList]];
    }
    [self dismissModalViewControllerAnimated:YES];
}

After the modal view is dismissed the table view is reloaded, and it now reflects the added item.

模态视图解除后,表格视图重新加载,现在它反映了添加的项目。

Batch Insertion, Deletion, and Reloading of Rows and Sections(批插入,删除和重新加载行和部分)

The UITableView class allows you to insert, delete, and reload a group of rows or sections at one time, animating the operations simultaneously in specified ways. The eight methods shown in Listing 7-7 pertain to batch insertion and deletion. Note that you can call these insertion and deletion methods outside of an animation block (as you do in the data source method tableView:commitEditingStyle:forRowAtIndexPath: as discussed in Inserting and Deleting Rows in Editing Mode).

UITableView类允许您一次插入,删除和重新加载一组行或部分,并以特定方式同时为这些操作设置动画。 清单7-7中显示的八个方法与批量插入和删除有关。 请注意,您可以在动画块之外调用这些插入和删除方法(就像在数据源方法tableView:commitEditingStyle:forRowAtIndexPath:那样tableView:commitEditingStyle:forRowAtIndexPath:如在编辑模式下插入和删除行所讨论的那样)。

Listing 7-7 Batch insertion and deletion methods

- (void)beginUpdates;
- (void)endUpdates;

- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

Note: The reloadSections:withRowAnimation: and reloadRowsAtIndexPaths:withRowAnimation: methods, which were introduced in iOS 3.0, allow you to request the table view to reload the data for specific sections and rows instead of loading the entire visible table view by calling reloadData.

注意:在iOS 3.0中引入的reloadSections:withRowAnimation:和reloadRowsAtIndexPaths:withRowAnimation:方法允许您请求表视图重新加载特定部分和行的数据,而不是通过调用reloadData加载整个可见表视图。

To animate a batch insertion, deletion, and reloading of rows and sections, call the corresponding methods within an animation block defined by successive calls to beginUpdates and endUpdates. If you don’t call the insertion, deletion, and reloading methods within this block, row and section indexes may be invalid. Calls to beginUpdates and endUpdates can be nested; all indexes are treated as if there were only the outer update block.

要对批次插入,删除和重新加载行和部分进行动画制作,请在连续调用beginUpdates和endUpdates所定义的动画块中调用相应的方法。 如果您不在此块中调用插入,删除和重新加载方法,则行和部分索引可能无效。 调用beginUpdates和endUpdates可以嵌套; 所有索引都被视为只有外部更新块。

At the conclusion of a block—that is, after endUpdates returns—the table view queries its data source and delegate as usual for row and section data. Thus the collection objects backing the table view should be updated to reflect the new or removed rows or sections.

在块结束时 - 即在endUpdates返回后 - 表视图查询它的 数据源和代理, 像往常一样为行和section数据。 因此,返回给table view的集合对象应该更新以反映新的或移除的行或节。

An Example of Batched Insertion and Deletion Operation(批量插入和删除操作的示例)

To insert and delete a group of rows and sections in a table view, first prepare the array (or arrays) that are the source of data for the sections and rows. After rows and sections are deleted and inserted, the resulting rows and sections are populated from this data store.

要在表视图中插入和删除一组行或部分,请首先准备作为部分和行的数据源的数组(或多个数组)。 在删除并插入行和节之后,将从此数据存储区填充生成的行和节。

Next, call the beginUpdates method, followed by invocations of insertRowsAtIndexPaths:withRowAnimation:, deleteRowsAtIndexPaths:withRowAnimation:, insertSections:withRowAnimation:, or deleteSections:withRowAnimation:. Conclude the animation block by calling endUpdates. Listing 7-8 illustrates this procedure.

接下来,调用beginUpdates方法,然后调用insertRowsAtIndexPaths:withRowAnimation: , deleteRowsAtIndexPaths:withRowAnimation: , insertSections:withRowAnimation:或deleteSections:withRowAnimation: 通过调用endUpdates结束动画块。 清单7-8说明了这个过程。

Listing 7-8 Inserting and deleting a block of rows in a table view

- (IBAction)insertAndDeleteRows:(id)sender {
    // original rows: Arizona, California, Delaware, New Jersey, Washington

    [states removeObjectAtIndex:4]; // Washington
    [states removeObjectAtIndex:2]; // Delaware
    [states insertObject:@"Alaska" atIndex:0];
    [states insertObject:@"Georgia" atIndex:3];
    [states insertObject:@"Virginia" atIndex:5];

    NSArray *deleteIndexPaths = [NSArray arrayWithObjects:
                                [NSIndexPath indexPathForRow:2 inSection:0],
                                [NSIndexPath indexPathForRow:4 inSection:0],
                                nil];
    NSArray *insertIndexPaths = [NSArray arrayWithObjects:
                                [NSIndexPath indexPathForRow:0 inSection:0],
                                [NSIndexPath indexPathForRow:3 inSection:0],
                                [NSIndexPath indexPathForRow:5 inSection:0],
                                nil];
    UITableView *tv = (UITableView *)self.view;

    [tv beginUpdates];
    [tv insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
    [tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
    [tv endUpdates];

    // ending rows: Alaska, Arizona, California, Georgia, New Jersey, Virginia
}

This example removes two strings from an array (and their corresponding rows) and inserts three strings into the array (along with their corresponding rows). The next section, Ordering of Operations and Index Paths, explains particular aspects of the row (or section) insertion and deletion behavior.

此示例从数组(及其相应的行)中移除两个字符串,并将三个字符串(及其相应的行)插入数组中。 下一部分“操作顺序和索引路径 ”解释了行(或部分)插入和删除行为的特定方面。

Ordering of Operations and Index Paths (操作顺序和索引路径)

You might have noticed something in the code shown in Listing 7-8 that seems peculiar. The code calls the deleteRowsAtIndexPaths:withRowAnimation:method after it calls insertRowsAtIndexPaths:withRowAnimation:. However, this is not the order in which UITableView completes the operations. It defers any insertions of rows or sections until after it has handled the deletions of rows or sections. The table view behaves the same way with reloading methods called inside an update block—the reload takes place with respect to the indexes of rows and sections before the animation block is executed. This behavior happens regardless of the ordering of the insertion, deletion, and reloading method calls.

清单7-8中显示的代码中您可能已经注意到了某些特殊情况。 代码在调用insertRowsAtIndexPaths:withRowAnimation:后调用deleteRowsAtIndexPaths:withRowAnimation:方法。 但是,这不是UITableView完成操作的顺序。 它延迟任何插入的行或部分,直到它处理了行或部分的删除。 表视图的行为与在更新块中调用的重新装入方法的行为相同 - 在执行动画块之前,将重新加载行和段的索引。 无论插入,删除和重新加载方法调用的顺序如何,都会发生此行为。

Deletion and reloading operations within an animation block specify which rows and sections in the original table should be removed or reloaded; insertions specify which rows and sections should be added to the resulting table. The index paths used to identify sections and rows follow this model. Inserting or removing an item in a mutable array, on the other hand, may affect the array index used for the successive insertion or removal operation; for example, if you insert an item at a certain index, the indexes of all subsequent items in the array are incremented.

动画块中的删除和重新加载操作指定应删除或重新加载原始表中的哪些行和部分; 插入指定应将哪些行和部分添加到结果表中。 用于标识部分和行的索引路径遵循此模型。 另一方面,插入或移除可变数组中的项目可能会影响用于连续插入或移除操作的数组索引; 例如,如果您在某个索引处插入项目,则数组中所有后续项目的索引都会增加。

An example is useful here. Say you have a table view with three sections, each with three rows. Then you implement the following animation block:

一个例子在这里很有用。 假设您有三个部分的表格视图,每个部分都有三行。 然后你实现下面的动画块:

  1. Begin updates.
  2. Delete row at index 1 of section at index 0.
  3. Delete section at index 1.
  4. Insert row at index 1 of section at index 1.
  5. End updates.

  6. 开始更新。

  7. 删除索引为0的部分的索引1处的行。
  8. 删除索引1处的部分。
  9. 在索引1的索引1处插入行。
  10. 结束更新。

Figure 7-2 illustrates what takes place after the animation block concludes.

图7-2说明了动画块结束后发生的事情。

Figure 7-2  Deletion of section and row and insertion of row

Managing the Reordering of Rows(管理行的重新排序)

A table view has an editing mode as well as its normal (selection) mode. When a table view goes into editing mode, it displays the editing and reordering controls associated with its rows. The reordering control allows the user to move a row to a different location in the table. As shown in Figure 8-1, the reordering control appears on the right side of the row.

表视图具有编辑模式以及其正常(选择)模式。 当表格视图进入编辑模式时,它将显示与其行相关的编辑和重新排序控件。 重新排序控件允许用户将一行移动到表中的不同位置。 如图8-1所示,重排序控件出现在行的右侧。

Figure 8-1  Reordering a row

When a table view enters editing mode and when users drag a reordering control, the table view sends a series of messages to its data source and delegate, but only if they implement these methods. These methods allow the data source and delegate to restrict whether and where a row can be moved as well to carry out the actual move operation. The following sections show you how to move rows around in a table view.

当表视图进入编辑模式并且用户拖动重新排序控件时,表视图向其发送一系列消息给它的数据源和代理 ,但前提是他们实现这些方法。 这些方法允许数据源和代理限制行和列的移动位置,以执行实际的移动操作。 以下部分将向您介绍如何在表格视图中移动行。

What Happens When a Row is Relocated(行重新定位时会发生什么)

A table view goes into editing mode when it receives a setEditing:animated: message. This normally happens when the user taps an Edit button in the navigation bar, but you can implement your own controls if you wish. In editing mode, a table view displays any reordering and editing controls that its delegate has assigned to each row. The delegate assigns the controls in tableView:cellForRowAtIndexPath: by setting the showsReorderControl property of UITableViewCell objects to YES. In order for reorder controls to appear, the data source must support reordering by implementing the tableView:moveRowAtIndexPath:toIndexPath: method.

表格视图在接收到setEditing:animated:消息时进入编辑模式。 这通常发生在用户点击导航栏中的编辑按钮时,但如果您愿意,您可以实现自己的控件。 在编辑模式下,表格视图显示其委托给每一行分配的任何重新排序和编辑控件。 委托在tableView:cellForRowAtIndexPath:分配控件tableView:cellForRowAtIndexPath:通过将UITableViewCell对象的showsReorderControl属性设置为 YES 。 为了重新排序控件出现,数据源必须通过实现tableView:moveRowAtIndexPath:toIndexPath:方法来支持重新排序。

Note: If a UIViewController object is managing the table view, it automatically receives a setEditing:animated:message when the Edit button is tapped. UITableViewController, a subclass of UIViewController, implements this method to update button state and invoke the table view’s version of the method. If you are using UIViewController to manage a table view, you need to implement the same behavior.

注意:如果一个UIViewController对象管理着表格视图,当点击Edit按钮时它会自动收到一个setEditing:animated:消息。 UITableViewController是UIViewController的子类,它实现了此方法来更新按钮状态并调用表视图的方法版本。 如果您使用UIViewController来管理表视图,则需要实现相同的行为。

When the table view receives setEditing:animated:, it sends the same message to the UITableViewCell object for each visible row. Then it sends a succession of messages to its data source and its delegate (if they implement the methods) as depicted in the diagram in Figure 8-2.

当表视图接收到setEditing:animated: ,它将相同的消息发送给每个可见行的UITableViewCell对象。 然后它将一连串的消息发送到它的数据源及其代理(如果他们实现了这些方法), 如图8-2所示 。

Figure 8-2  Calling sequence for reordering a row in a table view

When the table view receives the setEditing:animated: message, it resends the same message to the cell objects corresponding to its visible rows. After that, the sequence of messages is as follows:

当表视图接收到setEditing:animated:消息时,它会将相同的消息重新发送到与其可见行对应的单元对象。 之后,消息的顺序如下:

  1. The table view sends a tableView:canMoveRowAtIndexPath: message to its data source (if it implements the method). In this method the delegate may selectively exclude certain rows from showing the reordering control.
  2. The user drags a row by its reordering control up or down the table view. As the dragged row hovers over a part of the table view, the underlying row slides downward to show where the destination would be.
  3. Every time the dragged row is over a destination, the table view sendstableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: to its delegate (if it implements the method). In this method the delegate may reject the current destination for the dragged row and specify an alternative one.
  4. The table view sends tableView:moveRowAtIndexPath:toIndexPath: to its data source (if it implements the method). In this method the data source updates the data-model array that is the source of items for the table view, moving the item to a different location in the array.

  5. 表视图向其数据源发送tableView:canMoveRowAtIndexPath:消息(如果它实现了该方法)。 在这种方法中,委托人可以有选择地排除显示重新排序控制的某些行。

  6. 用户通过向上或向下的表格视图重新排序控件拖动一行。 当拖动的行悬停在表视图的一部分上时,底层行向下滑动以显示目标位置。
  7. 每当拖动的行超出目标时,表视图就会向其委托(如果它实现该方法)发送tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:在这种方法中,委托可以拒绝拖动行的当前目的地并指定一个替代的目标。
  8. 表视图将tableView:moveRowAtIndexPath:toIndexPath:发送到其数据源(如果它实现该方法)。 在此方法中,数据源更新作为表视图项目源的数据模型数组,将项目移动到数组中的其他位置。

Examples of Moving a Row(移动一行的示例)

This section comments on some sample code that illustrates the reordering steps enumerated in What Happens When a Row is Relocated. Listing 8-1 shows an implementation of tableView:canMoveRowAtIndexPath: that excludes the first row in the table view from being reordered (this row does not have a reordering control).

本节对一些示例代码进行了评论,这些代码演示了重新定位行时所发生的重新排序步骤。 清单8-1显示了tableView:canMoveRowAtIndexPath:一个实现tableView:canMoveRowAtIndexPath:表视图中的第一行从重新排序(该行没有重新排序控件)中排除。

Listing 8-1 Excluding a row from relocation

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) // Don't move the first row
      return NO;

   return YES;
}

When the user finishes dragging a row, it slides into its destination in the table view, which sends tableView:moveRowAtIndexPath:toIndexPath: to its data source. Listing 8-2 shows an implementation of this method.

当用户完成拖动一行时,它会在表视图中滑入其目标位置,该视图将tableView:moveRowAtIndexPath:toIndexPath:发送到其目标 数据源 。 清单8-2显示了这个方法的一个实现。

Listing 8-2 Updating the data-model array for the relocated row

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
    NSString *stringToMove = [self.reorderingRows objectAtIndex:sourceIndexPath.row];
    [self.reorderingRows removeObjectAtIndex:sourceIndexPath.row];
    [self.reorderingRows insertObject:stringToMove atIndex:destinationIndexPath.row];
}

The delegate can also retarget the proposed destination for a move to another row by implementing the tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: method. The following example restricts rows to relocation in their own group and prevents moves to the last row of a group (which is reserved for the add-item placeholder).

通过实现tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:方法,代理也可以将建议的目标移至另一行。 以下示例将行限制为在其自己的组中重定位,并阻止移动到组的最后一行(保留用于添加项占位符)。

Listing 8-3 Retargeting the destination row of a move operation

- (NSIndexPath *)tableView:(UITableView *)tableView
        targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
        toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
    NSDictionary *section = [data objectAtIndex:sourceIndexPath.section];
    NSUInteger sectionCount = [[section valueForKey:@"content"] count];
    if (sourceIndexPath.section != proposedDestinationIndexPath.section) {
        NSUInteger rowInSourceSection =
             (sourceIndexPath.section > proposedDestinationIndexPath.section) ?
               0 : sectionCount - 1;
        return [NSIndexPath indexPathForRow:rowInSourceSection inSection:sourceIndexPath.section];
    } else if (proposedDestinationIndexPath.row >= sectionCount) {
        return [NSIndexPath indexPathForRow:sectionCount - 1 inSection:sourceIndexPath.section];
    }
    // Allow the proposed destination.
    return proposedDestinationIndexPath;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【作者】 沈婕; 【导师】 肖双九; 陈刚; 【作者基本信息】 上海交通大学, 软件工程, 2010, 硕士 【摘要】 随着远程教育技术研究的不断深入,学生通过网络链接到站点学习的方式已不能满足现代社会人群对新型学习模式的要求。近年来,网络日益普及,网络课程直播已成为现代远程教育研究和应用中最新的发展趋势和研究热点之一,把网络教育同实时交互结合起来,将使网络教育步入一个新的阶段。网络课程直播中实时交互的出现,使教育者与受教育者之间的活动从异步发展到同步,逐渐摆脱了时间和空间的限制,进行随时随地的学习。本文提出了一种网络课程直播中实时交互的学习模式,设计并实现了网络远程教育系统中的课程直播子系统,为学生在远程学习模式下、直播学习的过程中,与老师交互提供了服务,同时有效地增强了远程学习环境下师生之间的互动,使远程学习者获得了更加完善的学习环境和学习支持服务,为实现更先进的远程教育系统提供了有利的条件。该子系统由网络课程直播模块和实时交互模块两大模块组成。其中网络课程直播模块采用了Windows Media直播方案和CSMX直播方案两种可选方案,实时交互模块中采用了Adobe的Flash Media Server和RTMP协议。该子系统的特点在于它同时结合了直播与交互两大模块,实时性更高,交互性更强。该子系统...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值