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
reloadDatamethod.你明確地調用了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 使用該列表要求你的數據源提供相關的單元格和補充視圖。 為了提供那些單元格和補充視圖,你的代碼必須完成以下工作:
-
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文件。)
-
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:方法可選,它由在使用的布局類型所決定。 兩種情況下,你對這些方法的實現都遵守一個非常簡單的模式:
-
Dequeue a cell or view of the appropriate type using the
dequeueReusableCellWithReuseIdentifier:forIndexPath:ordequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:method.使用
dequeueReusableCellWithReuseIdentifier:forIndexPath:或dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:方法 獲取一個適當類型的單元格或視圖。 -
Configure the view using the data at the specified index path.
使用指定索引路徑的數據配置視圖。
-
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, 你必須跟隨這些步驟:
-
Update the data in your data source object.
在你的數據資源對象里更新數據
-
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 returnYESfor the indicated cell.collectionView:shouldShowMenuForItemAtIndexPath: 方法必須為所指示的單元格返回YES.
-
The
collectionView:canPerformAction:forItemAtIndexPath:withSender:method must returnYESfor 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.
