IOS 集合视图指南3:设计你的数据源和代理

来源:互联网 发布:西门子编程电缆通讯 编辑:程序博客网 时间:2024/05/17 01:29

Designing Your Data Source and Delegate(设计你的数据源和代理)

Every collection view must have a data source object. The data source object is the content that your app displays. 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 how many items there are and which views to use when displaying those items.

The delegate object is an optional (but recommended) object that manages aspects related to the presentation of and interaction with your content. Although the delegate’s main job is to manage cell highlighting and selection, it 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.

每个集合视图都必须有一个数据源对象。数据源对象是你的应用要展示的内容。数据源对象可以是你的应用程序模型,或者可以使一个控制集合视图的视图控制器。数据源唯一的要求就是它必须提供集合视图需要的信息,比如一共需要多少个元素和什么时候展示这些元素

代理对象是一个可选对对象(但是建议),这个对象管理你的内容中的展示和互动。机关代理对象的主要功能是控制单元格的强调和选择状态,代理对象也可以推展提供的信息。比如,刘布局拓展了基本的代理行为来自定义布局标准,比如单元格的大小,和他们之间的距离。

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:

  • How many sections does the collection view contain?

  • For a given section, how many items does a section contain?

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

数据源对象是一个负责管理集合视图中你要展示的内容的对象。数据源对象必须遵守 UICollectionViewDataSource 协议,这个协议定义了你需要支持的基本行为和方法。数据源的工作提供集合视图关于下列问题的答案:
  • 集合视图内容有多少节
  • 每一节有多少元素呢
  • 每一节或者元素该怎样展现相应的内容呢?

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 a set of photos taken on the same day.

节和元素是集合视图基本的组织内容。一个集合视图通常至少会有一个节或者更多,每一节不包含元素或者包含更多元素。元素展现了你想展示的主要内容,同时节把元素分成了逻辑上的组。举个例子,一个照片应用可能会展示一个许多照片的相册,或者按照照相的日期来分配照片。

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 and decoration views, the index path contains whichever values were provided by the layout object. The meaning of the index paths attached to supplementary and decoration views is dependent on your app, though the first index corresponds to a specific section in the data source. These views’ index paths are more about identification than meaning, identifying which view of what kind is currently being considered. So, if for example you have supplementary views that create headers and footers for your sections as seen in the flow layout, the relevant information provided by the index path is the section referenced.

集合视图中相关的数据使用了NSIndexPath 对象。当你试图定位一个元素的时候,集合视图使用布局对象提供的索引轮径信息。对于元素,索引所经包含了节数和每一节的元素数,对于增补视图和装饰视图,索引轮径包含了布局对象提供的任何值。这意味着增补视图和装饰视图的索引依赖于你的应用,尽管第一个索引指向数据源中的一个特定的节。这些视图的索引路径关于标示多于他的意义,标示出现在正在考虑的视图是什么的种类的。所以,如果你的增补视图为你的流动布局的节创建了页眉和页脚,先关的信息是相关索引指出的。

Note: Although standard index paths support multiple levels, the collection view’s cells only supports index paths that are 2-levels deep with “section” and “item” parameters, much like the index paths for the UITableView class. Supplementary views and decoration views can have more complex index paths if necessary. Elements whose index paths are > 1 is interpreted to correspond to the section designated by the first index in the path. Traditionally, only a second index is necessary, but supplementary and decoration views are not restricted to just two. Keep this in mind when designing your data source.

提示:尽管标准的索引支持多层,但是集合视图的单元格仅仅支持两层的索引路径:“节”+“元素”,这一点很像表视图的索引路径。如果需要呢,增补视图和装饰视图可以有更复杂的索引视图。元素的路径>1说明相应的节指向第一个索引路径。通常意义上,两级所以是必要的,但是对于增补视图和装饰视图来说就不仅限于2了,当你设计你的数据源的时候注意这一点

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 according to the arrangement of layout objects

Designing Your Data Objects(设计你的数据对象)

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

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 to calculate how many objects there are in total and to obtain views for elements that are currently onscreen. If the layout object relies only on data from your data objects, performance could be severely impacted when the data source contains thousands of objects.

当设计你的数据结构的时候,你可以从设置一个简单的数组开始然后根据需要添加有效的数据结构。通常,你的数据对象不应该是一个性能瓶颈。集合视同通访问你的数据源仅仅通过计算一共有多少元素,并且获取当前屏幕需要的数据对象。如果布局对象只依赖于你的数据对象的话,当你的数据对象包含上千条数据的时候性能就会受到大幅度影响。

Telling the Collection View About Your Content(告诉集合视图关于你的内容)

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:

  • The collection view is displayed for the first time.

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

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

  • The collection view delegate executes a block using performBatchUpdates:completion: or any of the move, insert, or delete methods.

集合视图问数据源的问题中有集合视图包含了多少节,每一节包含多少数据项。当下列时间发生的时候,集合视图问你的数据源要信息

  • 集合视图第一次展示的时候
  • 你给集合视图指定了一个不同的数据源的时候
  • 你明确要求集合视图调用reloadData方法的时候
  • 集合视图代理执行performBatchUpdates:completion:使用的代码块的时候,或者任何移动,插入,或者删除方法执行的时候。

You provide the number of sections using the numberOfSectionsInCollectionView: method, and the number of items in each section using the collectionView: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.

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 in Listing 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.)


你使用numberOfSectionsInCollectionView:来提供节的数目,用collectionView:numberOfItemsInSection:方法来提供每节的数据项数目。你必须实现collectionView:numberOfItemsInSection: 方法,但是如果你的集合视图只有一节, numberOfSectionsInCollectionView: 方法就可以选择实现。这两个方法的返回值都是integer类型的。

如果你像图2-2那样实现你的数据源,那么你就可以像列表2-1那样简单的实现数据源方法,这些代码中,_data是保存着数据源的节的定义数组的自定义成员变量。获得数组中节的数据 ,获取子数组中数据项的数目。(当然你的代码中应该做足错误检查以确保你的返回值是可用的)。

Listing 2-1  Providing the section and item counts

- (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 does not track 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.

After your data source reports how many sections and items it manages, the collection view asks the layout object to provide layout attributes for the collection view’s content. At some point, the collection view asks the layout object to provide the list of elements in a specific rectangle (often this is the visible rectangle). 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:

  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.)

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

你的数据源的另一个重要功能是给集合视图提供视图以展示你的内容。集合视图并不跟踪你的应用的内容。它仅仅是接受你给它的视图并且在现在的布局信息应用在上面,因此,所有展示的视图都需要你来来负责。
在你的数据源报告了它有多少节和项需要管理之后,集合视图请求布局对象提供布局属性用以集合视图的内容中。集合视图要求布局对象提供这些列表项的布局(通常是可视窗口),用一个特定矩形的一些点来表示。集合视图使用哪个列表来请求你的数据源来匹配单元格和增补视图。为了提供单元格和增补视图,你在代码中需要做如下事情:
  1. 把你的模板单元格和增补视图嵌入你的故事版中(或者,为每种单元格和视图注册一个类或者nib文件)。
  2. 在你的数据源中,在请求的时候列出合适的单元格并设置。

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, 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.

Reuse identifiers make it possible to register multiple types of cells and multiple types of supplementary views. A reuse identifier is 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 then pass the appropriate reuse identifier to the dequeue method.

为了确保单元格和增补视图被最有效的使用,集合视图将会为你创建这些对象,每个集合视图维护一个未使用的单元格和增补视图的内部队列。你不需要自己创建对象,仅仅向集合视图请求你需要的对象就可以。如果呢一个视图对象等待在服用队列里面,集合视图回准备好他并马上把它返回给你,如果这个视图没有在复用队列里面,集合视图就会用注册的类或者nib文件创建一个新的视图对象并返回给你。这样,每次你列出一个单元格或者视图,你都会获得一个现成的对象。

复用标识可以让注册多种类型的单元格和增补视图成为可能。复用标识是一个可以区分你注册的单元格和视图类型的字符串。字符串的内容仅仅和你的数据源对象有关。但是当你请求一个视图或一个对象的时候,你可以使用提供的索引路径决定你想要使用那种类型的视图和单元格,然后传递适当的重用标识给列出函数。

Registering Your Cells and Supplementary Views(注册你的单元格和增补视图)

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

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.

  • 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.

  • 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 View Cell 把它放到你的集合视图上。设置自定义类同时为你的单元格设置合适的复用视图标识。
  • 对于增补视图,从对象库中拖拽一个Collection Reusable View把它放在你的集合视图上。设置自定义类同时为你的视图设置合适的复用视图标识。

Configure cells programmatically. Use either the registerClass:forCellWithReuseIdentifier: or registerNib: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.

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.

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 kindsof supplementary views it supports. For example, the UICollectionViewFlowLayout class supports two kinds of supplementary views: a section header view and a section footer view. To identify these two types of views, it defines the string constants UICollectionElementKindSectionHeader and UICollectionElementKindSectionFooter. 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.

用编程方法设置单元格。使用registerClass:forCellWithReuseIdentifier:方法或 registerNib:forCellWithReuseIdentifier: 方法让你的单元格和复用标识发生联系。你可以在调用这些方法作为父视图控制器初始化的一部分。
用编程的方法设置增补视图。使用 registerClass:forSupplementaryViewOfKind:withReuseIdentifier:  方法或registerNib:forSupplementaryViewOfKind:withReuseIdentifier: 方法让你的视图和你的复用标识发生联系。你可以在调用这些方法作为父视图控制器初始化的一部分。
尽管你只能为你的单元格设置唯一一个复用标识,增补视图却可以要求你指定一个额外的标识作为类型字符串。每个布局对象负责定义他支持的增补对象类型。比如,UICollectionViewFlowLayout类支持两种增补视图:一节的页眉视图,和一节的页脚视图。为了表示这两种视图的类型,UICollectionViewFlowLayout 定义了两个字符串 UICollectionElementKindSectionHeaderUICollectionElementKindSectionFooter.。在布局中,布局对象包含了类型字符串和其他视图类型的布局属性。之后结合视图会把信息发送至你的数据源,你的数据源之后会使用这两种字符串和复用标识符来决定哪个视图对象会出列并返回。

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. After you’ve registered, you can dequeue as many cells or views as needed without reregistering them. It’s 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(列出并设置单元格和视图)

Your data source object is responsible for providing cells and supplementary views when asked for them by the collection view. The UICollectionViewDataSource protocol contains two methods for this purpose: collectionView:cellForItemAtIndexPath: and collectionView:viewForSupplementaryElementOfKind:atIndexPath:. Because cells are a required element of a collection view, your data source must implement the collectionView:cellForItemAtIndexPath: method, but the collectionView: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:

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

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

  3. Return the view.

你的数据源对象负责提供单元格和增补视图当想集合视图请求他们的时候。UICollectionViewDataSource 协议包含了两种实现这种目的的方法collectionView:cellForItemAtIndexPath::和collectionView:viewForSupplementaryElementOfKind:atIndexPath:.。因为单元格是集合视图需要的一个元素,你的数据源必须完成collectionView:cellForItemAtIndexPath:  方法,但是collectionView:viewForSupplementaryElementOfKind:atIndexPath: 方法是可选的,选择依据使用的布局类型。在两种情况下,你可以用非常简单的模式实现这些方法:
  1. 列出合适类型的单元格或视图用如下方法:dequeueReusableCellWithReuseIdentifier:forIndexPath: dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:
  2. 使用指定路径的数据来设置的视图
  3. 返回视图。

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.

The cell returned to you 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. In contrast, an item that wasn’t created from scratch but that was instead retrieved from a reuse queue may already contain data from a previous usage. In that case, 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 to reset properties to default values and perform any additional cleanup.

After your data source dequeues the view, it configures 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 then apply that object’s data to the view. After you configure 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.

设计列出过程的目的,是为你减轻创建你自己创建单元格的责任。只要你之前注册了单元格或者视图,出列方法就保证不会返回空。如果没有相应单元格或视图在复用队列中,出列函数就会简单的使用你的故事版或者类或你定义的类或nib文件来来创建一个对象。

列出返回的单元格应该是原始状态的准备好设置数据的。对于一个必须被创建的单元格或者视图,列出过程使用正常的程序(从故事版或者nib文件读取视图,或者使用 initWithFrame: 方法创建和初始化一个新的实例)创建和初始化它。相反的,对于一个不是一开始就创建的项而是从复用队列里面获取一个之前用过已经包含数据的项呢。在这种情况下,列出方法调用数据项的perpareForReuse方法返回数据项本身同时恢复初始状态。当你自定义一个一个单元格或者视图类的时候,你需要重写这个方法来重设属性的默认值同时执行额外的清理。
在你的数据源列出视图之后,这个视图就已经被新的数据设置好了。你可以使用索引路径传递数据给你的数据源方法定位合适的数据对象,同时把数据应用到视图上。在你设置这个视图之后,再把它返回。列表2-2展示了一个简单的例子告诉你如何设置cell。当你列出这个cell之后,使用信息设置cell的自定义label,然后返回cell。

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;
}

Note: When returning views from your datasource, always return a valid view. Returning nil, even if for some reason the view that is being asked for should not be displayed, causes an assertion and your app terminates because the layout object expects valid views to be returned by these methods.



未完待续……

本文原创,转载请注明出处:http://blog.csdn.net/zhenggaoxing/article/details/44301733

































0 0