快轉到主要內容

Swift TableView 與 Collection View

·1672 字·4 分鐘
Denny Cheng / 月月冬瓜
作者
Denny Cheng / 月月冬瓜
獸控兼工程師兼鍵盤武術家

TableView
#

IndexPath: Section & Row
#

Section and Row

看圖應該可以明瞭Section和Row的含意,Apple把tableView劃分為section,每個section下又有row。
在IOS中有一個叫做IndexPath的類別專門儲存這兩個值,在tableView以後要override的方法會經常看到。

必定要override的method: 幾個section 幾個row
#

使用tableView必須指定DataSource和Delegate (若是TableViewController則已經指定好自己作為這兩個protocol的實作)
其中有些function是必定要override的

  1. 總共有幾個section
  2. 每個section裡面有幾個row

在這個範例中,我指定兩個section,section 0有2 row,section 1有3 row。

// 總共幾個section
override func numberOfSections(in tableView: UITableView) -> Int {
  return 2
}

// 每個section裡面有幾個row
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
	if section == 0{
  	return 2
  }
  else{
  	return 3
	}
}

某一列的內容
#

但是只有這樣,顯示出來會是空白一片,所以必須要回傳顯示的內容。
在做這個項目之前,必須先到storyBoard,將tableView裏頭的cell指定一個identifier,否則會錯誤。
在此我指定cell的identifier為myCell

Identifier

然後才可以在程式碼中使用myCell

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)

  // 顯示目前是第幾Row
  cell.textLabel?.text = "Row " + String(indexPath.row)
  return cell
}
目前畫面

Section Title
#

但是這樣少了每個section的title,沒有段落感,所以用以下的code加上。

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
  if section == 0{
  	return "Section 0"
  }
  else{
  	return "Section 1"
  }
}
目前畫面

點擊某列跳轉到下一個Controller,並傳值
#

請參閱之前的傳值方法,我們選用方法一。這邊要補充的是:可以藉由tableview中的indexPathForselectedRow屬性來得知哪一列被選中。

  1. 在prepare裡裡面進行傳值的預備動作。
  2. 轉型Controller,這樣才可以塞property
    • 在此我們假設SecondViewController中有selectedIndexPath這個屬性。
  3. 可以藉由tableView中的indexPathForselectedRow來判定哪個Row被選中。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  if let indexPath = tableView.indexPathForSelectedRow{
    let controller = segue.destination as! SecondViewController
    controller.selectedIndexPath = indexPath
  }
}

滑動刪除
#

只要複寫方法即可,記得必須同時刪除實際的Array以及畫面上的元件,否則會造成畫面與實際資料不同步的問題。

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
  if editingStyle == .delete {
    // 刪除元素
    myArray.remove(at: indexPath.row)
    // 刪除畫面上的元素
    tableView.deleteRows(at: [indexPath], with: .fade)
  } 
}

但使用這個方法,只能滑出一個按鈕。如果想滑動時有多個按鈕可以選擇請看下節。

自訂滑動選項
#

如果想要自訂滑動按鈕的文字樣式,或是想指定多個滑動按鈕,那就不能使用上述的複寫方法。而必須改用下面這個。

  1. 複寫方法
  2. 自訂UITableViewRowAction
  3. 回傳UITableViewRowAction陣列

因為此方法回傳陣列,所以可以塞進多個按鈕。

override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let addAction = UITableViewRowAction(style: .normal, title: "insert") { 
        	(action, indexPath) in
          // 下面可寫插入元素的動作  
        }
        
        let deleteAction = UITableViewRowAction(style: .default, title: "delete") { 
        	(action, indexPath) in
          // 下面可寫刪除元素的動作  
        }
        return [deleteAction, addAction]
    }

更新畫面的method
#

TableView裡面有三個method可以更新畫面。列表如下

  • 刪除:需提供IndexPath的陣列,一次可以刪一串。
  • 插入:需提供IndexPath的陣列,一次可以加入一串。
  • 重新整理

程式碼如下

tableView.deleteRows(at: [indexPath], with: .fade)
tableView.insertRows(at: [indexPath], with: .fade)
tableView.reloadData()

更換位置
#

下拉更新
#

常常會看到有APP在tableView下拉時畫面會開始轉圈圈,然後更新,現在試著製作這個效果。

  1. 在StoryBoard中點選TableViewController。
  2. 將Refreshing設定成enable
  3. 此時左方會出現一個Refresh Control,將其拉到程式碼中,選擇Action,動作為value changed。
    • 設定
  4. 接下來就可以對剛剛拉出的action做一些事,做完後使用self.refreshControl?.endRefreshing(),就可以結束轉圈圈的畫面。
@IBAction func reload(_ sender: AnyObject) {
    // do something
    self.refreshControl?.endRefreshing()
}

搜尋
#

let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
self.tableView.tableHeaderView = searchController.searchBar
searchController.dimsBackgroundDuringPresentation = false

UICollectionView
#

UICollectionViewDelegateFlowLayout
#

可以調整cell的大小