Designing Your Data Source and Delegate

设计你的数据源和委托

Every collection view must have a data source object providing it with content to display. The data source object is an object that your app provides. It could be an object from your app’s data model or it could be the view controller that manages the collection view. The only requirement of the data source is that it must be able to provide information that the collection view needs, such as the total number of items and the views to use when displaying those items.

每个collection view都必须有一个提供显示内容的数据源对象。 数据源对象是应用程序提供的对象。它可以是应用程序的数据模型中获得的对象,也可以是管理collection view的视图控制器。数据源的唯一要求是它必须能给collection view提供所需的信息,比如当collection view显示数据项时,需要知道的数据项和所需视图的总数量等。

The delegate object is an optional (but recommended) object that manages aspects related to the presentation of and interaction with your content. The main job of the delegate is to manage cell highlighting and selection. However, the delegate can be extended to provide additional information. For example, the flow layout extends the basic delegate behavior to customize layout metrics such as the size of cells and the spacing between them.

委托对象是一个可选(但是推荐)的对象,它管理所要显示内容的相关外形以及跟内容交互等各个方面。 委托的主要工作是管理单元格的高亮和selection(选区). 然而,委托也能被扩展来提供额外的信息。比如,流布局扩展基本的委托行为来自定义布局标准(layout metrics),比如单元格尺寸和单元格之间的间距。

The Data Source Manages Your Content

一、数据源管理你的内容

The data source object is the object responsible for managing the content you are presenting using a collection view. The data source object must conform to the UICollectionViewDataSource protocol, which defines the basic behavior and methods that you must support. The job of the data source is to provide the collection view with answers to the following questions:

数据源对象负责管理你想使用collection view显示的内容。 数据源对象必须遵守UICollectionViewDataSource 协议,该协议定义了你必须支持的各种基本行为和方法。 数据源的工作是给collection view提供以下问题的答案:

  • How many sections does the collection view contain?

    collection view 包含了几个section?

  • For a given section, how many items are in that section?

    每个section里有几项数据内容?

  • For a given section or item, what views should be used to display the corresponding content?

    对于每个section 或 数据项, 该用什么视图来显示相关的内容?

Sections and items are the fundamental organizing principle for collection view content. A collection view typically has at least one section and may contain more. Each section, in turn, contains zero or more items. Items represent the main content you want to present, whereas sections organize those items into logical groups. For example, a photo app might use sections to represent a single album of photos or the set of photos taken on the same day.

Sections(段)和items(项)是collection view内容的基本组织原则。一个collection view通常至少有一个或多个段。每个段都依次包含0个或多个数据项。数据项代表你想要显示的内容,段把那些数据项组织成逻辑组。比如,一个照片应用程序可能使用不同的段来代表一个照片相册或是按同一天拍摄分组的一些照片集。

The collection view refers to the data it contains using NSIndexPath objects. When trying to locate an item, the collection view uses the index path information provided to it by the layout object. For items, the index path contains a section number and an item number. For supplementary views, the index path contains whichever values were provided by the layout object.

collection view的引用数据包含它使用的NSIndexPath 对象。 当你想要定位一个数据项时,collection view使用布局对象提供给它的索引路径信息。 对于数据项,索引路径包含了一个段号和一个项目号。 对于补充视图,索引路径包含布局对象提供的那个值。

No matter how you arrange the sections and items in your data object, the visual presentation of those sections and items is still determined by the layout object. Different layout objects could present section and item data very differently, as shown in Figure 2-1. In this figure, the flow layout object arranges the sections vertically with each successive section below the previous one. A custom layout could position the sections in a nonlinear arrangement, demonstrating again the separation of the layout from the actual data.

不管你如何排列数据对象的段和数据项,那些段和数据项最终的可视化呈现任然由布局对象决定。不同的布局对象可以呈现出完全不同的段和项数据,正如图2-1中所示。 在图中,流布局对象垂直排列每个段。自定义布局可以不在直线上放置这些段,再次证明布局和实际数据的分离。

Figure 2-1  Sections arranged differently by different layout objects

Designing Your Data Objects

1、设计你的数据对象

An efficient data source uses sections and items to help organize its underlying data objects. Organizing your data into sections and items makes it much easier to implement your data source methods later. And because your data source methods are called frequently, you want to make sure that your implementations of those methods are able to retrieve data as quickly as possible.

一个高效的数据源使用段和数据项来帮助组织它的后台数据对象。 把数据组织进段和数据项让实现你的数据源方法更加容易。并且因为你的数据源方法会被经常调用,所以你想要确保你对这些方法的实现能够尽可能快的检索数据。

One simple solution (but certainly not the only solution) is for your data source to use a set of nested arrays, as shown in Figure 2-2. In this configuration, a top-level array contains one or more arrays representing the sections of your data source. Each section array then contains the data items for that section. Finding an item in a section is a matter of retrieving its section array and then retrieving an item from that array. This type of arrangement makes it easy to manage moderately sized collections of items and retrieve individual items on demand.

一个简单的解决办法(但肯定不是唯一的解决办法)是让数据源使用一个嵌套的数组集,正如图2-2中所示。 在该配置中,一个高层次的数组包含了一个或多个数组,这些被包含的数组代表你的数据源段。然后每个段数组包含了那个段的所有数据项。在段里查找一个数据项是检索它的段数组,然后从该数组检索那个数据项。 该排列方式类型很适合用来管理中等规模的项目集合,也很适合用来根据需要检索单个数据项。

Figure 2-2  Arranging data objects using nested arrays

图 2-2 使用嵌套数组排列数据对象

When designing your data structures, you can always start with a simple set of arrays and move to a more efficient structure as needed. In general, your data objects should never be a performance bottleneck. The collection view usually accesses your data source only when it needs to know how many objects there are in total and when it needs views for elements that are currently onscreen. However, if the layout object relies on data from your data objects, performance could be severely impacted when the data source contains thousands of objects. For this reason, you should avoid implementing custom layout objects that rely on data from your data source. Instead, you should design your layout objects to lay out content independently from your data objects.

当你设计你的数据结构时,你可以总是以一个简单的数据集开始,然后根据需要把它移入更有效地结构中。 基本上,你的数据对象绝不应该是一个性能瓶颈。 collection view通常只在它需要的时候才访问你的数据源:1. 需要知道总共有多少数据对象的时候,2. 需要为当前在屏幕上的元素(elements)提供视图的时候. 然而,如果布局对象依赖数据对象中的数据,当数据源包含成千上万的对象时,性能就会受到很大的影响。由于这个原因,你应该避免实现依赖数据源数据的自定义布局对象。相反,你应该设计和数据对象相分离的布局对象。

Telling the Collection View About Your Content

2、告诉Collection View你的内容

Among the questions asked of your data source by the collection view are how many sections it contains and how many items each section contains. The collection view asks your data source to provide this information when any of the following actions occur:

在collection view询问数据源的问题当中,有数据源包含多少段以及每个段包含多少数据项的问题。 collection view要求数据源在以下情况发生时提供这些信息给它:

  • The collection view is displayed for the first time.

    collection view显示第一次显示。

  • You assign a different data source object to the collection view.

    你给collection view分配了一个不同的数据源对象。

  • You explicitly call the collection view’s reloadData method.

    你明确地调用了collection view的reloadData

You provide the number of sections using the numberOfSectionsInCollectionView: method and the number of items in each section using thecollectionView:numberOfItemsInSection: method. You must implement the collectionView:numberOfItemsInSection: method but if your collection view has only one section, implementing the numberOfSectionsInCollectionView: method is optional. Both methods return integer values with the appropriate information.

你使用 numberOfSectionsInCollectionView: 方法提供段的数量,使用collectionView:numberOfItemsInSection: 方法来提供每段的项目数量。你必须实现collectionView:numberOfItemsInSection:方法,但是如果你只有一个段,numberOfSectionsInCollectionView:方法的实现是可选的。两个方法都返回一个整数值,代表合适的信息。

If you implemented your data source as shown in Figure 2-2, the implementation of your data source methods could be as simple as those shown inListing 2-1. In this code, the _data variable is a custom member variable of the data source that stores the top-level array of sections. Obtaining the count of that array yields the number of sections. Obtaining the count of one of the subarrays yields the number of items in the section. (Of course, your own code should do whatever error checking is needed to ensure that the values returned are valid.)

如果你实现图Figure 2-2那样的数据源,你的数据源实现方法可能跟列表2-1那样简单。 在列表2-1中,_data 变量是数据源的一个自定义成员变量,它存储了段的高层数组。 获取数组的数目产生段的数目。 获取其中一个子数组的数目产生段中的数据项数量。(当然,你应该在代码中完成任何需要的错误检测来确保返回的值是有效的)

Listing 2-1  Providing the section and item counts.

列表 2-1 提供段数和项数

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView {
    // _data is a class member variable that contains one array per section.
    return [_data count];
}
 
- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {
    NSArray* sectionArray = [_data objectAtIndex:section];
    return [sectionArray count];
}

Configuring Cells and Supplementary Views

二、配置单元格和补充视图

Another important task of your data source is to provide the views that the collection view uses to display your content. The collection view knows nothing about any of your app’s content. It simply takes the views you give it and applies the current layout information to them. Therefore, everything that is displayed by the views is your responsibility.

数据源的另一个重要任务是提供collection view用来显示内容的各种视图。collection view完全不知道关于应用程序内容的任何信息。它只是简单的获取你提供给它的视图,并对这些视图应用当前的布局信息。因此,提供视图显示的任何东西都是你的责任。

After your data source reports how many sections and items it manages, the collection view asks the layout object to perform the layout operation. At some point, the collection view asks the layout object to provide the list of elements in a specific rectangle (often the visible rectangle but not necessarily so). The collection view uses that list to ask your data source for the corresponding cells and supplementary views. To provide those cells and supplementary views, your code must do the following:

当数据源报告完段和数据项的数量之后,collection view要求布局对象来执行布局操作。 在一些时候,collection view要求布局对象提供一个指定矩形(常常是可视但不需要的矩形)中的元素列表。collection view 使用该列表要求你的数据源提供相关的单元格和补充视图。 为了提供那些单元格和补充视图,你的代码必须完成以下工作:

  1. Embed your template cells and views in your storyboard file. (Alternatively, register a class or nib file for each type of supported cell or view.)

    在你的故事板文件里嵌入你的单元格模板和视图。(或者,为每种类型的单元格或视图注册一个类或nib文件。)

  2. In your data source, dequeue and configure the appropriate cell or view when asked.

    被请求时,在数据源里取回并配置相应的单元格或视图。

To ensure that cells and supplementary views are used in the most efficient way possible, the collection view assumes the responsibility of creating those objects for you. Each collection view maintains internal queues of currently unused cells and supplementary views. Instead of creating objects yourself, you simply ask the collection view to provide you with the view you want. If one is waiting on a reuse queue, the collection view prepares it and returns it to you quickly. If one is not waiting, the collection view uses the registered class or nib file to create a new one and return it to you. Thus, every time you dequeue a cell or view, you always get a ready-to-use object.

为了确保单元格和补充视图以最有效的方式被应用,collection view承担了为你创建那些对象的责任。 每个collection view维护了当前不使用的单元格和补充视图的内容队列。 你不需要自己创建那些对象,当你需要视图时,你只需要向collection view要求相应的视图即可。 如果一个请求正在一个可重用队列中等待,collection view准备一个视图并快速地返回给你。因此,每次你取回一个单元格或视图时,你得到的总是可以马上就可以用的对象。

Reuse identifiers make it possible to register multiple types of cells and multiple types of supplementary views. A reuse identifier is just a string that you use to distinguish between your registered cell and view types. The contents of the string are relevant only to your data source object. But when asked for a view or cell, you can use the provided index path to determine which type of view or cell you might want and pass the appropriate reuse identifier to the dequeue method.

重用标识符让注册多种类型的单元格和多种类型的补充视图成为可能。重用标识符是一个字符串,用来辨别你注册的单元格和视图类型。字符串的内容只跟你的数据源对象有关。但是当被请求一个视图或单元格时,你可以根据提供的索引路径来决定你想要哪种类型的视图或单元格,然后给获取方法传递合适的重用标识符。

Registering Your Cells and Supplementary Views

1、注册你的单元格和补充视图

You can configure the cells and views of your collection view programmatically or in your app’s storyboard file.

你可以通过程序或应用程序的故事板文件来配置collection view的单元格和视图。

  • To configure cells and views in your storyboard:

    在故事板里配置单元格和视图

    When configuring cells and supplementary views in a storyboard, you do so by dragging the item onto your collection view and configuring it there. This creates a relationship between the collection view and the corresponding cell or view.

     当你在故事板里配置单元格和补充视图时,你通过往collection view里拖拉item来完成,并在那里配置它。 这样做就给collection view 和 相应的单元格和视图创建了一个对应关系。

    • For cells, drag a Collection View Cell from the object library and drop it on to your collection view. Set the custom class and the collection reusable view identifier of your cell to appropriate values.

       对于单元格,从对象库(object library)里拖拉一个Collection View Cell, 放在你的collection view里。设置单元格的自定义类和collection 可重用视图标识符。

    • For supplementary views, drag a Collection Reusable View from the object library and drop it on to your collection view. Set the custom class and the collection reusable view identifier of your view to appropriate values.

       对于补充视图,从对象库里拖拉一个Collection Reusable View,并把它放在collection view里。 设置视图的自定义类和collection可重用视图标识符。

  • To configure cells programmatically, use either the registerClass:forCellWithReuseIdentifier:
    orregisterNib:forCellWithReuseIdentifier: method to associate your cell with a reuse identifier. You might call these methods as part of the parent view controller’s initialization process.

  •  要想通过程序配置单元格,使用registerClass:forCellWithReuseIdentifier: 
    或 registerNib:forCellWithReuseIdentifier: 方法来关联单元格和一个重用标识符。你可以在父视图控制器初始化进程中作为一部分调用这些方法。

  • To configure supplementary views programmatically, use either the registerClass:forSupplementaryViewOfKind:withReuseIdentifier: 
    orregisterNib:forSupplementaryViewOfKind:withReuseIdentifier: method to associate each kind of view with a reuse identifier. You might call these methods as part of the parent view controller’s initialization process.

     要想通过程序配置补充视图,使用registerClass:forSupplementaryViewOfKind:withReuseIdentifier: 
    或 registerNib:forSupplementaryViewOfKind:withReuseIdentifier: 方法来关联每个视图和重用标识符。 你可以在父视图控制器的初始化进程里作为一部分调用这些方法。

Although you register cells using only a reuse identifier, supplementary views require that you specify an additional identifier known as a kind string. Each layout object is responsible for defining the kinds of supplementary views it supports. For example, the UICollectionViewFlowLayout class supports two kinds of supplementary views: a section header view and a section footer view. As a result, it defines the string constantsUICollectionElementKindSectionHeader and UICollectionElementKindSectionFooter to identify these two types of views. During layout, the layout object includes the kind string with the other layout attributes for that view type. The collection view then passes the information along to your data source. Your data source then uses both the kind string and the reuse identifier to decide which view object to dequeue and return.

尽管你只是用一个重用标识符注册所有单元格,但是补充视图要求一个额外指定的标识符--被称为种类字符串(kind string)。 每个布局对象负责定义它支持的补充视图的种类(kinds)。 比如, UICollectionViewFlowLayout 类支持两种类型的补充视图:一个节头视图和一个节尾视图。 所以它定义了UICollectionElementKindSectionHeader 和 UICollectionElementKindSectionFooter 两个常量来区分这两种类型的视图。 布局期间,布局对象包含了该类型字符串以及该视图类型的其它布局属性。然后collection view 让这些信息随着你的数据源一起传递。 你的数据源然后使用类型字符串和重用标识符来决定取回哪个视图对象。

Note: If you implement your own custom layouts, you are responsible for defining the kinds of supplementary views your layout supports. A layout may support any number of supplementary views, each with its own kind string. For more information about defining custom layouts, see “Creating Custom Layouts.”

注意:如果你实现你自定义的布局,由你负责为你的补充视图定义它支持的种类。 一个布局可能支持任何数量的补充视图,它们每个都有自己的类型字符串。关于定义自定义布局的更多信息,请看 “Creating Custom Layouts.”

Registration is a one-time event that must take place before you attempt to dequeue any cells or views. Once registered, you can dequeue as many cells or views as needed without reregistering them. It is not recommended that you change the registration information after dequeueing one or more items. It is better to register your cells and views once and be done with it.

注册是一个一次性事件,你必须在尝试获取任何单元格或视图之前完成它。一旦完成注册,你就可以根据需要获取单元格或视图,而不需要重新注册它们。不推荐在获取一个或多个数据项之后更改注册信息。只注册一次单元格和视图,并使用它完成工作是更好的选择。

Dequeueing and Configuring Cells and Views

2、出队和配置单元格和视图

Your data source object is responsible for providing cells and supplementary views when asked for them by the collection view. TheUICollectionViewDataSource protocol contains two methods for this purpose: collectionView:cellForItemAtIndexPath: andcollectionView:viewForSupplementaryElementOfKind:atIndexPath:. Because cells are a required element of a collection view, your data source must implement the collectionView:cellForItemAtIndexPath: method, but thecollectionView:viewForSupplementaryElementOfKind:atIndexPath: method is optional and dependent on the type of layout in use. In both cases, your implementation of these methods follows a very simple pattern:

当collection view请求单元格和补充视图时,由你的数据源对象负责提供它们。UICollectionViewDataSource 协议包含了2种方法来实现这个目的:collectionView:cellForItemAtIndexPath:  和

collectionView:viewForSupplementaryElementOfKind:atIndexPath:. 因为单元格是一个collection view必须的元素, 你的数据源必须实现collectionView:cellForItemAtIndexPath: 方法,但是collectionView:viewForSupplementaryElementOfKind:atIndexPath:方法可选,它由在使用的布局类型所决定。 两种情况下,你对这些方法的实现都遵守一个非常简单的模式:

  1. Dequeue a cell or view of the appropriate type using the dequeueReusableCellWithReuseIdentifier:forIndexPath:
     ordequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: method.

    使用dequeueReusableCellWithReuseIdentifier:forIndexPath: 或 

    dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: 方法 获取一个适当类型的单元格或视图。

  2. Configure the view using the data at the specified index path.

     使用指定索引路径的数据配置视图。

  3. Return the view.

     返回视图。

The dequeueing process is designed to relieve you of the responsibility of having to create a cell or view yourself. As long as you registered a cell or view previously, the dequeue methods are guaranteed to never return nil. If there is no cell or view of the given type on a reuse queue, the dequeue method simply creates one using your storyboard or using the class or nib file you registered.

出队进程(dequeueing process)的设计是为了减轻你不得不自己创建一个单元格或视图的责任。只要你之前注册了一个单元格或视图,dequeue方法就绝对不会返回nil值。如果在一个重用队列里没有给定类型的单元格或视图,dequeue方法会使用你的故事板或使用你注册的类或nib文件简单创建一个。

The cell you get back from the dequeueing process should be in a pristine state and ready to be configured with new data. For a cell or view that must be created, the dequeueing process creates and initializes it using the normal processes—that is, by loading the view from a storyboard or nib file or by creating a new instance and initializing it using the initWithFrame: method. However, an item that was not created from scratch, but was instead retrieved from a reuse queue, may already contain data from a previous usage. In that case, the dequeue methods call the prepareForReuse method of the item to give it a chance to return itself to a pristine state. When you implement a custom cell or view class, you can override this method and use it to reset properties to default values and perform any additional cleanup.

你从出队进程中返回的单元格应该已经在初始状态并准备好配置新数据。对于必须被创建的单元格或视图,出队进程通过正常过程来创建并初始化它---换句话说,就是通过从一个故事板或nib文件载入或通过创建并用initWithFrame:方法初始化来获取一个新实例。但是,如果数据项不是从头开始创建,而是从一个重用队列里取回的,则可能已经包含了上一次使用返回的数据。 在这种情况下,dequeue方法调用数据项的prepareForReuse方法来让它返回自身以进入初始状态。当你实现一个自定义单元格或视图类时,你可以重载该方法并使用它来重新设置属性到默认值,并执行任何额外的清除工作。

After dequeueing the view, all your data source has to do is configure the view with its new data. You can use the index path passed to your data source methods to locate the appropriate data object and apply that object’s data to the view. After configuring the view, return it from your method and you are done. Listing 2-2 shows a simple example of how to configure a cell. After dequeueing the cell, the method sets the cell’s custom label using the information about the cell’s location and then returns the cell.

当视图完成出队之后,所有的数据源必须做的是用它的新数据配置视图。 你可以使用传递给数据源方法的索引路径来定位相应的数据对象,并给该对象应用新值。 配置完视图之后,从你的方法中返回视图。列表2-2 显示了一个如何配置一个单元格的例子。 单元格完成出队之后,方法用单元格的位置信息设置单元格的自定义标签并返回该单元格。

Listing 2-2  Configuring a custom cell

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath {
   MyCustomCell* newCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:MyCellID
                                                                          forIndexPath:indexPath];
 
   newCell.cellLabel.text = [NSString stringWithFormat:@"Section:%d, Item:%d", indexPath.section, indexPath.item];
   return newCell;
}

Inserting, Deleting, and Moving Sections and Items

三、插入,删除,以及移动Sections 和 Items

To insert, delete, or move a single section or item, you must follow these steps:

要想插入,删除或移动单个section 或 item, 你必须跟随这些步骤:

  1. Update the data in your data source object.

     在你的数据资源对象里更新数据

  2. Call the appropriate method of the collection view to insert or delete the section or item.

     调用collection view相应的方法来插入或删除section 或 item.

It is critical that you update your data source before notifying the collection view of any changes. The collection view methods assume that your data source contains the currently correct data. If it does not, the collection view might receive the wrong set of items from your data source or ask for items that are not there and crash your app.

在通知collection view任何更改之前更新你的数据源是至关重要的。collection view方法假设你的数据源包含了当前正确的数据。如果它没有包含,collection view可能从你的数据源接收到错误的数据项集或请求并不存在的数据项从而导致应用程序崩溃。

When you add, delete, or move a single item programmatically, the collection view’s methods automatically create animations to reflect the changes. If you want to animate multiple changes together, though, you must perform all insert, delete, or move calls inside a block and pass that block to theperformBatchUpdates:completion: method. The batch update process then animates all of your changes at the same time and you can freely mix calls to insert, delete, or move items within the same block.

当你通过程序添加,删除或移动一个数据项时,collection view的方法自动创建动画来反映这些改变。 如果你想一起动画多个改变,你必须在一个块里执行所有的插入,删除或移动操作,并把该块传递给performBatchUpdates:completion: 方法。然后批量更新过程同时动画所有的改变,你可以在同一个块里自由的混合调用插入,删除,或移动数据项操作。

Listing 2-3 shows a simple example of how to perform a batch update to delete the currently selected items. The block passed to theperformBatchUpdates:completion: method first calls a custom method to update the data source. It then tells the collection view to delete the items. Both the update block and the completion block you provide are executed synchronously, although the animations themselves are performed by the system on a background thread.

列表2-3 显示了一个简单例子,关于如何执行一个删除当前被选择数据项的批量更新。被传递到 performBatchUpdates:completion: 方法的块首先调用一个自定义方法来更新数据源。 然后它高数collection view删除这些数据项。 你提供的更新块和完成块都是同步执行的,尽管它们自己的动画是由系统在一个后台线程里被执行。

Listing 2-3  Deleting the selected items

[self.collectionView performBatchUpdates:^{
   NSArray* itemPaths = [self.collectionView indexPathsForSelectedItems];
 
   // Delete the items from the data source.
   [self deleteItemsFromDataSourceAtIndexPaths:itemPaths];
 
   // Now delete the items from the collection view.
   [self.collectionView deleteItemsAtIndexPaths:tempArray];
} completion:nil];

Managing the Visual State for Selections and Highlights

四、管理选区和高亮显示的视觉状态

Collection views support single-item selection by default and can be configured to support multiple-item selection or have selections disabled altogether. The collection view detects taps inside its bounds and highlights or selects the corresponding cell accordingly. For the most part, the collection view modifies only the properties of a cell to indicate that it is selected or highlighted; it does not change the visual appearance of your cells, with one exception. If a cell’s selectedBackgroundView property contains a valid view, the collection view shows that view when the cell is highlighted or selected.

Collection views默认支持单项选择(single-item selection), 它们能被配置成支持多项选择或多项取消选择。 collection view 侦测在它边界里的轻击(taps),并相应的选择或高亮相关单元格。对于大部分情况,collection view只修改一个单元格的属性来声明该单元格已被选择或高亮;它不改变所有单元格的视觉外形,只有一种情况例外。如果一个单元格的selectedBackgroundView 属性有一个有效视图,当单元格被高亮后选择,collection view显示那个视图。

There is a subtle but important distinction between a cell’s highlighted state and its selected state. The highlighted state is a transitional state that you can use to apply visible highlights to the cell while the user’s finger is still touching the device. This state is set to YES only while the collection view is tracking touch events over the cell. When touch events stop, the highlighted state returns to the value NO. By contrast, the selected state changes only after a series of touch events has ended—specifically, when those touch events indicated that the user tried to select the cell.

一个单元格的高亮状态和被选择状态之间有一个微妙但是很重要的区别。高亮状态是一个过渡状态,当用户的手指还触摸在设备屏幕上时,你可以使用它来给单元格应用视觉高亮效果。只有当collection view一直在单元格上追踪触摸事件时,该状态被设置为YES. 当触摸结束时,高亮状态恢复到NO。 相反,被选择状态只在一些列的触摸事件结束以后才改变---特别是,当这些触摸事件表明用户试着选择该单元格时。

Figure 2-3 illustrates the series of steps that occurs when a user touches an unselected cell. The initial touch-down event causes the collection view to change the highlighted state of the cell to YES, although doing so does not automatically change the appearance of the cell. If the final touch up event occurs in the cell, the highlighted state returns to NO and the collection view changes the selected state to YES. Changing the selected state does cause the collection view to display the view in the cell’s selectedBackgroundView property, but this is the only visual change that the collection view makes to the cell. Any other visual changes must be made by your delegate object.

图2-3 演示了当用户触摸一个未被选择单元格时发生的一些列步骤。 初始触摸down事件导致collection view改变单元格的高亮状态为YES, 尽管这样做并不自动改变单元格的外形。 如果最后的触摸up事件在单元格里发生,高亮状态回到NO,collection view 把被选择状态设置为YES。 改变被选择状态确实导致collection view在单元格的selectedBackgroundView属性影响下显示视图, 但是这是collection view对单元格做的唯一视觉改变。 其它任何视觉改变都必须由你的委托对象来完成。

Figure 2-3  Tracking touches in a cell

图2-3 在一个单元格里追踪触摸

If you prefer to draw the selection state of a cell yourself, you can leave the selectedBackgroundView property set to nil and apply any visual changes to the cell using your delegate object. You would apply the visual changes in the collectionView:didSelectItemAtIndexPath: method and remove them in the collectionView:didDeselectItemAtIndexPath: method.

如果你更喜欢自己来绘制一个单元格的选择状态,你可以把selectedBackgroundView属性设置为nil, 并用你的委托对象来给单元格应用任何可视化改变。 你应该在collectionView:didSelectItemAtIndexPath: 方法里应用这些可视化改变,在collectionView:didDeselectItemAtIndexPath: 方法里删除这些改变。

If you prefer to draw the highlight state yourself, you can override the collectionView:didHighlightItemAtIndexPath: andcollectionView:didUnhighlightItemAtIndexPath:
 delegate methods and use them to apply your highlights. If you also specified a view in theselectedBackgroundView property, you should make your changes to the content view of the cell to ensure your changes are visible. Listing 2-4 shows a simple way of changing the highlight using the content view’s background color.

如果你更喜欢自己绘制高亮状态,你可以重载collectionView:didHighlightItemAtIndexPath: 和 

collectionView:didUnhighlightItemAtIndexPath: 委托方法并把它们应用到你的高亮。如果你还在selectedBackgroundView属性里指定了一个视图, 你应该给单元格的内容视图(content view)做这些改变来确保你的所有更改都可见。 列表2-4 显示了使用content view的背景颜色来改变高亮的一种简单方法。

Listing 2-4  Applying a temporary highlight to a cell

列表 2-4 给一个单元格应用一个临时高亮

- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = [UIColor blueColor];
}
 
- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = nil;
}

Whether the user is selecting or deselecting a cell, the cell’s selected state is always the last thing to change. Taps in a cell always result in changes to the cell’s highlighted state first. Only after the tap sequence ends, and any highlights applied during that sequence are removed, does the selected state of the cell change. When designing your cells, you should make sure that the visual appearance of your highlights and selected state do not conflict in unintended ways.

当用户选择或取消选择一个单元格时,单元格的被选择状态永远是最后被改变的。单元格里的轻击事件永远导致单元格的高亮状态首先被改变。 只有当轻击序列都结束时,并且任何在那个序列被应用的高效都没移除之后,被选择状态才被改变。 当你设计你的单元格时,你应该确保你的高亮可视化外形跟被选择状态不会以意想不到方式发生冲突。

Showing the Edit Menu for a Cell

五、为单元格显示编辑按钮

When the user performs a long-tap gesture on a cell, the collection view attempts to display an Edit menu for that cell. The Edit menu can be used to cut, copy, and paste cells in the collection view. Several conditions must be met before the Edit menu can be displayed:

当用户在一个单元格上执行一个长事件轻触手势时,collection view尝试给那个单元格显示一个编辑按钮。 编辑按钮可以被用来在collection view里剪切,拷贝,以及粘贴单元格。在编辑按钮被显示之前,一些情况肯定会出现:

  • The delegate must implement all three methods related to handling actions:

    委托必须实现所有跟处理动作有关的3个方法: 

  • The collectionView:shouldShowMenuForItemAtIndexPath: method must return YES for the indicated cell.

     collectionView:shouldShowMenuForItemAtIndexPath: 方法必须为所指示的单元格返回YES.

  • The collectionView:canPerformAction:forItemAtIndexPath:withSender: method must return YES for at least one of the desired actions. The collection view supports the following actions:

    collectionView:canPerformAction:forItemAtIndexPath:withSender: 方法必须至少为一个所需的动作返回YES。 collection view支持以下动作: 

    • cut:

    • copy:

    • paste:

If these conditions are met and the user chooses an action from the menu, the collection view calls the delegate’scollectionView:performAction:forItemAtIndexPath:withSender: method to perform the action on the indicated item.

如果遇到这些问题,用户从按钮选择了一个操作,collection view调用委托的collectionView:performAction:forItemAtIndexPath:withSender: 来执行所指示数据项上的操作。

Listing 2-5 shows how to prevent one of the menu items from appearing. In this example, thecollectionView:canPerformAction:forItemAtIndexPath:withSender: method prevents the Cut menu item from appearing in the Edit menu. It enables the Copy and Paste items so that the user can insert content.

列表2-5 显示了如何防止菜单项之一出现。在该例子中,collectionView:canPerformAction:forItemAtIndexPath:withSender: 方法阻止剪切按钮出现在编辑菜单里。 编辑菜单允许拷贝和粘贴数据项,所以用户能够插入内容。

Listing 2-5  Selectively disabling actions in the Edit menu

列表2-5 选择性的禁用编辑菜单里的动作

- (BOOL)collectionView:(UICollectionView *)collectionView
        canPerformAction:(SEL)action
        forItemAtIndexPath:(NSIndexPath *)indexPath
        withSender:(id)sender {
   // Support only copying nad pasting of cells.
   if ([NSStringFromSelector(action) isEqualToString:@"copy:"]
      || [NSStringFromSelector(action) isEqualToString:@"paste:"])
      return YES;
 
   // Prevent all other actions.
   return NO;
}

For more information on working with the pasteboard commands, see Text, Web, and Editing Programming Guide for iOS.

更多关于粘贴板命令的信息,请看Text, Web, and Editing Programming Guide for iOS.

posted on 2013-07-11 15:09  cainiaozhang  阅读(3828)  评论(1编辑  收藏  举报