[翻譯]在objective c建立自定義collection view樣式

建立自定義collection樣式

蘋果在ios6中新增了一個更加易於建立和管理複雜用戶界面的類:collection view。在此以前ios6上面用於展現多項列表的是table view,雖然名稱是表格但它展現信息的形式並非表格形式,而是垂直列表,固然在實際設計展現垂直可滾動的文本型列表時很是有用,可是你會遇到許多垂直可滾動的列表所存在的侷限性,好比不能水平滾動,每行只有一項單元格,沒有複雜的樣式,在ios6以前你想要作到這些事情,只能靠自定義擴展。 在ios6 平臺介紹了一個新的類:collection view ,它擁有全部table view的功能,新增了許多展現信息的方式而且變得更加易用。 table view 的功能很是有限,而collection view 則能夠進行更加靈活的設計。它能夠按不一樣的形式展現表格內容:橫向或垂直滾動,列表或表格或任何你想要的自定義樣式,它還提供了很是靈活的樣式定義去爲每個 collection view 設定不一樣的展示形式,蘋果提供了一個簡單的例子來講明這個樣式功能將會知足許多人的須要,這個 flow layout 是基於每一行定義的佈局,每個單元均可以線性地展示在上一個單元后面,直到該行填充完以後,切換到下一行。這將會根據他的配置項來決定時垂直仍是水平滾動,或者網格形式(每行有多個單元格)。html

即便你的樣式不那麼簡單,你仍然可使用collection view。只須要提供自定義樣式便可。本文將會提供一個建立自定義樣式的例子,我不打算提供一個完整的collection view 例子或者樣式, 蘋果的手冊和WWDC視頻(205-219)已經提供了很是好的樣例。本文將會基於一個已完成的例子進行自定義樣式調整。ios

如今讓咱們來看看咱們想要自定義的樣式:一些單元格很大,而其餘的會小一些。 img 要支持這個樣式,咱們須要繼承並重寫 UICollectionViewLayout 告訴collection 要如何去展示單元格。緩存

首先咱們來定義collection view 的大小。咱們的樣式將會展現12個單元格在一頁,假如多於12個單元格,咱們將會在按水平方向滾動展現。因此計算一頁會有多少單元格展現是一件很是簡單的事情,咱們只須要根據collection view 的數據源(data source)提供的數據進行計算就能夠知道咱們總共須要展現多少頁。我把這個 collectionVIewCOntenSize 方法重寫了:性能優化

`- (CGSize)collectionViewContentSize { // Ask the data source how many items there are (assume a single section) id<UICollectionViewDataSource> dataSource = self.collectionView.dataSource; int numberOfItems = [dataSource collectionView:self.collectionView numberOfItemsInSection:0];app

// Determine how many pages are needed
int numberOfPages = ceil((float)numberOfItems / (float)kNumberOfItemsPerPage);

// Set the size
float pageWidth = self.collectionView.frame.size.width;
float pageHeight = self.collectionView.frame.size.height;
CGSize contentSize = CGSizeMake(numberOfPages * pageWidth, pageHeight);

return contentSize;

}`框架

下一步咱們要自定義每一頁中須要展現的單元格樣式,對於每個單元格,咱們的樣式要處理的事情:佈局

  • 這個單元格在第幾頁
  • 這個單元格在第幾行(或者它的垂直偏移量是多少)
  • 這個單元格在該行第幾位(或者它的水平偏移量是多少)
  • 它的大小

collection view 有兩種方式獲取樣式的信息,當你須要在一個新的區域展現collection的時候,它將會向-layoutAttributesForElementsInRect:傳遞一個 rect 定義以後註冊一個collection view。它也能夠按照相對的佈局信息,經過調用-layoutAttributesForItemAtIndexPath: 來返回每個單元格須要樣式信息。對於佈局來講,咱們也可使用惰性計算而沒必要計算每個元素的佈局信息,例如:咱們的應用會有大約100個單元格的佈局,爲了簡單起見,我老是預先計算好佈局的屬性而且緩存起來,這樣的處理最終被證實是一個很不錯的性能優化方式。性能

根據蘋果提供的文檔,樣式將會在調用這幾個方法的時候被初始化:優化

  1. prepareLayout
  2. collectionViewContentSize
  3. layoutAttributesForElementsInRect

因此咱們將會在初始化 -prepareLayout 方法的時候計算全部的樣式信息。有很是多的方式能夠計算每個樣式信息,可是在這裏我將會使用一個簡單的例子:this

由於我知道每一頁給定的單元格式12,因此我決定根據按頁給予每個元素定義不一樣的參數,我能夠設置每一頁的展現單元格爲水平方式,當存在多個頁面時,將會按照頁面寬度去初始化collection view的寬度。這意味着我要在得到元素以後計算出正確的頁數,這裏咱們知道每一頁須要展現12個單元格,我能夠將總數除以12來得到正確的頁數。而後根據每個單元格的水平和垂直偏移量來計算它的位置,這裏能夠看出來水平方向只有4個單元格,而其中三個比較小,因此咱們可使用很簡單的switch語句來肯定每個單元格的樣式參數,而後就能夠根據這些值爲每個單元格建立一個佈局屬性對象。

`

  • (CustomFlowLayoutAttributes *)layoutAttributesForItemAtIndex:(int)index { NSIndexPath *path = [NSIndexPath indexPathForItem:index inSection:0]; CustomFlowLayoutAttributes *attributes = (CustomFlowLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:path];

    // Figure out what page this item is on int pageNumber = floor((float)index / (float)kNumberOfItemsPerPage);

    // Set the horizontal offset for the start of the page float pageWidth = self.collectionView.frame.size.width; float horizontalOffset = pageNumber * pageWidth;

    // Now, determine which position this cell occupies on the page.
    int indexOnPage = index % kNumberOfItemsPerPage;

    int column = 0; switch (indexOnPage) { case 0: case 3: case 5: column = 0; break; case 1: case 4: case 6: case 9: column = 1; break; case 2: case 7: case 10: column = 2; break; case 8: case 11: column = 3; break; default: column = 0; break; }

    int row = 0; switch (indexOnPage) { case 0: case 1: case 2: row = 0; break; case 3: case 4: row = 1; break; case 5: case 6: case 7: case 8: row = 2; break; case 9: case 10: case 11: row = 3; break; default: row = 0; break; }

    horizontalOffset = horizontalOffset + ((kColumnWidth + kPadding) * column); float verticalOffset = (kRowHeight + kPadding) * row;

    // finally, determine the size of the cell. float width = 0.0; float height = 0.0;

    switch (indexOnPage) { case 2: width = kLargeCellWidth; height = kLargeCellHeight; break; case 5: width = kColumnWidth; height = kMediumCellHeight; break; default: width = kColumnWidth; height = kRowHeight; break; }

    CGRect frame = CGRectMake(horizontalOffset, verticalOffset, width, height); [attributes setFrame:frame];

    return attributes; }`

這樣定義樣式計算的屬性值以後,咱們就能夠很簡單的經過向layoutAttributesForItemAtIndexPath: 或者 layoutAttributesForElementsInRect發送請求獲取每個單元格的屬性。

這就是爲使用 collection view 所須要自定義的樣式,它將會容許collection view 在指定單元格的位置顯示正確數目的單元格。這裏只須要咱們作一個簡單的事情:繼承並顯示咱們的單元格。就會得到三個不一樣大小的單元格框架(一些小的,一個很大而且佔用兩行的單元格和一箇中等大小的)這三種不一樣的單元格就像咱們代碼所設定的同樣顯示。最後咱們在每個單元格類中定義單元格的內容,咱們就能夠獲得自定義樣式並將這些樣式信息添加到每個單元格屬性中。

要作到這個咱們只須要: 繼承 UICollectionViewLayoutAttributes 去建立一個自定義的子類並將咱們想要擴展的信息添加到子類中傳遞給咱們的單元格(在這裏咱們用 cell 類型)。注意建立的時候要添加一個property, 我須要重寫一個父類接口的 copyWithZone: 來保證添加的 property 可以使用NSCopying 協議去建立一個 UICollectionView的副本傳遞給單元格。

實現這個UICollectionViewLayoutAttriButes的方法,咱們自定義的樣式子類將會返回咱們想要的樣式屬性。

`

  • (Class)layoutAttributesClass { return [CustomFlowLayoutAttributes class]; }`

而後我只須要在我實現UICollectionVIewCell子類中計算附加的樣式屬性值和實現一些邏輯,就能夠在接收這些佈局參數以後,獲得一個我須要的佈局。

雖然蘋果的文檔(以及無數次提到的WWDC視頻)已經很是描述得很是清楚。可是對大多數人來講,只要使用標準的流式佈局就應該足夠了,若是你發如今流式佈局的狀況下不能徹底知足你的需求的時候,也許你須要建立一個自定義的佈局,這隻須要佔用你一點點的工做時間就能夠實現。

原帖來自:http://stripysock.com.au/blog/2013/2/21/creating-a-custom-collection-view-layout

相關文章
相關標籤/搜索