利用 enum 配合我們的 UITableView / UICollectionView,寫出一個淺顯易懂的程式碼吧!
在我們編寫 UITableView/UICollectionView 的時候,因為我們的 Cell 都是根據 IndexPath
來決定它的位置,必須告訴程式碼是哪個 section
哪個 row
我們才能夠對它進行操作,所以在程式碼我們常常會使用 if
或是 switch
語法來判斷。但是因為 IndexPath
這兩者的數值都是 Int
,如果是個人開發的時候可能還好,但是一旦要跟別人共同開發的時候,其他開發人員可能不了解這些數字代表什麼或者是哪一個 Cell。所以下面的教學是我個人在開發這種表單視圖時會使用的方式。
首先我們來看一下我們的設計圖( 雖然已經是成品了,就假裝是設計圖 )
你可以看見我們有使用 UICollectionView 來開發出上面這個畫面,那這個時候的你是怎麼規劃開發這個畫面的呢?用 1分鐘的時間思考看看 ?
當然你用什麼方式都無所謂,只要能夠在正確的位置告訴 CollectionView 的 Datasource 是哪個 CollectionViewCell 就行了,不管你是用 IndexPath
、section
或是 row
來區分他。但是這邊我想有概念的人可能會使用 section
方式來區分這兩種不同的 Cell,如此一來你不需要對 row
進行額外的計算,只需要知道是哪個 section
即可,再傳遞給 Cell 資料時也不需要額外的計算。
如此一來,你的 cellForItemAt 這段程式碼可能會類似於下面這段:
switch IndexPath.section {
case 0:
// 建置 UIColletionViewCell
return TitleCell
case 1:
// 建置 UIColletionViewCell
return ItemCell
default:
return UICollectionViewCell()
}
因為我們這個範例的 section
很明顯的就只有兩種,如果今天我們同時有很多個 section
並且從畫面上也很難分出是哪個 section
的 Cell,那麼我們該怎麼透過程式碼讓其他開發者了解這是哪個 section
呢?
這時,我們的 enum 這個型別就能派上用場了,以這個例子我們可以定義一個 enum 來分別這兩個 section
,並且將這個 enum 的型別定義為 Int
:
enum HomeSections: Int {
case banner = 0, items
}
我們定義 HomeSections
型別為 Int 的原因是因為我們要透過其 rawValue
來實例化我們的 HomeSections
,我們可以透過 HomeSections(rawValue:)
傳入我們的 section
來產生我們 HomeSections
的 case,如果傳入的 rawValue
不包含在我們的 case 中,則會得到一個 nil
。
如此以來我們就能透過這個 HomeSections
初始化的結果來判斷是哪個 case ,如此一來我們也能清楚了解是哪個 section
下的操作:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let homeSection = HomeSections(rawValue: indexPath.section) else {
return UICollectionViewCell()
}
switch homeSection {
case .banner:
let titleCell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeCellID.title, for: indexPath)
return titleCell
case .items:
let itemCell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeCellID.item, for: indexPath)
return itemCell
}
}
如此一來我們只要透過程式碼就能夠了解他是在對哪個 section
進行操作,不用再去猜 section
的數字是代表哪個項目,透過 HomeSections
來賦予它一個 case,如此一來就能清楚地看出是哪個 section
下的操作。
當然如果也需要判斷你的 row
個別是哪個項目,你也可以建立一個 enum 將他們初始化成你的 case,透過 switch 這個 enum 下的結果,對每個 case 進行個別的操作,下方程式碼是我點選下方白色按鈕進行個別的操作:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// 判斷點擊區域是否為 .item
if HomeSections(rawValue: indexPath.section) == .items {
guard let selectedItem = HomeItems(rawValue: indexPath.row) else { return }
switch selectedItem {
case .bookShelf:
self.performSegue(withIdentifier: HomeSegueID.bookshelfSegue, sender: nil)
case .wishingBook:
self.performSegue(withIdentifier: HomeSegueID.wishingBookSegue, sender: nil)
case .scanner:
self.performSegue(withIdentifier: HomeSegueID.scannerSegue, sender: nil)
case .location:
self.performSegue(withIdentifier: HomeSegueID.mapSegue, sender: nil)
default:
break
}
}
}
如此一來,不管我對哪個 case 進行操作,你應該都看得出來我是在對哪個按鈕項目進行操作吧,雖然都是執行 performSegue
,而我們的 Segue 的 identifier 也透過 HomeSegueID
這個 struct 包裝起來,並且使用 static
方式宣告,所以也不需要害怕打錯程式碼囉~
雖然大家再開發的過程大部分的時間都還是有時程或是其他的因素存在,所以導致可能 IndexPath
還是大部分都還是使用數字來判斷,但是如果我們能夠使用 enum 的方式來編寫程式碼,不僅清楚明白 IndexPath
代表著什麼,同時有多個開發者也能有個易讀的程式碼架構能夠維護。