iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
0
自我挑戰組

iOS 新手開發的大小事系列 第 23

Day 23: [UIKit] UICollectionView 介紹

  • 分享至 

  • xImage
  •  

前言

因為沒有使用過 collection view,所以想趁此機會,來練習一下,原本以為 table view 熟練了之後,collection view 也用同樣的方式理解,但發現其實這兩種的本質上似乎不太一樣,collection view 與 collection view layout 有相當的關聯性,最後在實作的過程也遇到奇怪的問題,果然還是要實作,才會真正練習到解決問題的能力啊!(補充:經過比對,應該是 Xcode 11 的問題,但實際原因不明)


概觀


圖:集合視圖 (CollectionView) 顯示的樣式

將集合視圖添加到用戶界面時,應用的主要工作是管理與該集合視圖相關的數據。集合視圖從數據源物件獲取數據,該數據源對像是符合 UICollectionViewDataSource 協定的物件,由應用程式提供。集合視圖中的數據被組織成單獨的項目,然後可以被分組為多個部分以進行展示。項目是呈現的最小數據單位。例如,在照片應用中,一個項目可能是單個圖像。集合視圖使用單元在屏幕上顯示項目,該單元是數據源配置和提供的類 UICollectionViewCell 的實例。

除了其單元格外,集合視圖還可以使用其他類型的視圖來呈現數據。這些補充視圖可以是與各個單元格分離但仍傳達某種信息的部分頁眉和頁腳之類的東西。對補充視圖的支持是可選的,由集合視圖的佈局物件定義,該佈局物件還負責定義這些視圖的位置。 除了將其嵌入到用戶界面中之外,還使用 UICollectionView 物件的方法來確保項目的可視表示與數據源物件中的順序匹配。因此,無論何時在集合中添加,刪除或重新排列數據,都可以使用此類的方法來插入,刪除和重新排列相應的單元格。可以使用集合視圖物件來管理選定的項目,儘管對於這種行為,集合視圖使用其關聯的委任物件。


集合視圖和佈局物件 (Collection Views and Layout Objects)

與集合視圖關聯的一個非常重要的物件是佈局物件,它是類 UICollectionViewLayout 的子類。佈局物件負責定義集合視圖中所有單元格和補充視圖的組織和位置。儘管佈局物件定義了它們的位置,但實際上並沒有將該訊息應用於相應的視圖。因為單元格和輔助視圖的創建涉及集合視圖與數據源物件之間的協調,所以集合視圖實際上將佈局訊息應用於視圖。因此,從某種意義上說,佈局物件就像另一個數據源,僅提供視覺訊息而非項目數據。

通常,在創建集合視圖時可以指定佈局物件,但也可以動態更改集合視圖的佈局。佈局物件儲存在屬性 collectionViewLayout 中。設置此屬性可立即直接更新佈局,而無需對更改進行動畫處理。如果要為更改設置動畫,則必須調用setCollectionViewLayout(_: animated: completion:)方法。 如果要創建交互式過渡(由手勢識別器或觸摸事件驅動),請使用 startInteractiveTransition(to: completion:)方法更改佈局物件。該方法將安裝一個中間佈局物件,其目的是與手勢識別器或事件處理代碼一起使用以追蹤過渡進度。當事件處理代碼確定轉換已完成時,它將調用finishInteractiveTransition()cancelInteractiveTransition() 方法以刪除中間佈局物件並安裝預期的目標佈局物件。


創建單元格和補充視圖 (Creating Cells and Supplementary Views)

集合視圖的數據源物件既提供項目的內容,又提供用於顯示該內容的視圖。集合視圖首次加載其內容時,會要求其數據源為每個可見項提供一個視圖。為了簡化代碼的創建過程,集合視圖要求視圖出隊,而不是在代碼中創建視圖。有兩種使視圖出隊的方法。您使用的視圖取決於請求的視圖類型:

  • 使用 dequeueReusableCell(withReuseIdentifier: for:) 獲取集合視圖中某個項目的單元格。
  • 使用 dequeueReusableSupplementaryView(ofKind: withReuseIdentifier: for:) 方法來獲取佈局物件請求的補充視圖。

在調用這兩種方法中的任何一種之前,必須告訴集合視圖(如果不存在)如何創建相應的視圖。為此,必須在集合視圖中註冊一個類或一個 nib 文件。例如,在註冊單元格時,可以使用 register(_ cellClass: forCellWithReuseIdentifier:)register(_ nib: forCellWithReuseIdentifier:) 方法。作為註冊過程的一部分,可以指定標識視圖用途的重用標識符。這與稍後使視圖出隊時使用的字符串相同。在委任方法中使適當的視圖出隊後,配置其內容並將其返回到集合視圖以供使用。從佈局物件獲取佈局信息後,集合視圖將其應用於視圖並顯示它。


界面生成器屬性 (Interface Builder Attributes)

  • Items: 原型單元的數量。此屬性控制指定數量的原型單元,提供在 Storyboard 中配置。集合視圖必須始終至少具有一個單元格,並且可能具有多個單元格,用於顯示不同類型的內容或以不同方式顯示相同內容。
  • Layout: 要使用的佈局對象。使用此控件可以在 UICollectionViewFlowLayout 物件和自定義佈局物件之間進行選擇。選擇流程佈局 (flow layout) 後,還可以配置集合視圖內容的滾動方向,以及流程佈局是否具有頁首和頁腳視圖。啟用頁首和頁腳視圖會為 Storyboard 添加可重用的視圖,可以將其與頁首和頁腳內容一起配置。還可以以編程方式創建這些視圖。選擇自定義佈局時,必須指定要使用的 UICollectionViewLayout 子類。

實際範例

與 table view 類似,可以使用兩種方法來建立 collection view,就不在此贅述。對於 collection view 並不是很熟悉,就使用直接在 view controller 加入的方式來當作範例,不過在實作過程中,踩到了一個奇怪的坑,還不明白是為什麼,後面會敘述如何解決這奇怪的坑。

先在 view controller 加入 collection view

Layout 會預設成 Flow 模式,並把 Items 改為 1,就類似 table view 的 cell

給定一個 Identifier 的名稱

在 cell 中加入一個 image view 並設定好與 cell 的 contraints,如下圖

加入一個新的檔案來設定 cell

把 UIImageView 的 outlets 拉進 collection cell 中

class ImageCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var imageView: UIImageView!
    
}

View controller 程式碼部分,加入 UICollectionViewDelegate 和 UICollectionViewDataSource 的委任

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
// 省略
}

view controller 的程式

let picArray = ["1", "2", "3", "4", "5"]
    
@IBOutlet weak var collectionView: UICollectionView!
    
override func viewDidLoad() {
    super.viewDidLoad()
        
    collectionView.delegate = self
    collectionView.dataSource = self
           
}
    
func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}
    
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return picArray.count
}
    
    
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "data", for: indexPath) as! ImageCollectionViewCell
    cell.imageView.image = UIImage(named: picArray[indexPath.row])
        
    return cell
}

實際執行結果

後記

奇怪的坑
原本不管怎麼設定好 cell 的大小,圖片始終會變成原本的大小,就設的 constraints 都像是沒有作用一樣,最後是點選 UIImageView,把 view 中的 Layout 由 Automatic 改成 Translates Mask Into Constraints 解決問題。


上一篇
Day 22: [UIKit] UITableView 介紹 -2
下一篇
Day24: [UIKit] UISearchBar 介紹
系列文
iOS 新手開發的大小事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言