(Swift) 如何在 TableView 中,上下移動 cell

目的:上下移動 cell 的同時,並同步修改原本資料的順序

第一步

新增一個 TableViewController ,並且在 TableViewController 中新增 NavigationController 後,新增一個 Bar Button Item 到 TableViewController 中

記得要將 NavigationController 中的 Shows Toolbar 打勾,不然 Bar Button Item 將不會顯示,或者能夠透過以下程式碼來讓 Toolbar 顯示

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

navigationController?.isToolbarHidden = false
}

第二步

準備顯示在 TableView 中 Cell 的資料,並且撰寫 Cell 顯示的程式碼

favorite:顯示的資料

  • favorit 中 第一個 key “favorite” 之後會加入 favorit 中 第二個 key “banks” 的第一間銀行

keys:保存 favorite 中每個 key 的 Array 名稱

    var favorite = [
"favorite": [

],

"banks": [
"玉山銀行",
"台新銀行",
"富邦銀行",
"王道銀行"
]
]

var keys = [String]()

使用以下程式碼將 favorite 的 keys 加入到 keys 中,並且 keys 必須使用 sort(by: > ) ,不然顯示的 section 順序會跑掉

override func viewDidLoad() {
super.viewDidLoad()
// 將 favorite 的 keys 加入到 keys 中
for i in favorite.keys {
keys.append(i)
}
// 必須加入 sort 不然會亂跳
keys.sort(by: >)

...
}

接下來在 viewDidLoad 中新增以下程式碼,讓 favorite 顯示順序第一的銀行

override func viewDidLoad() {
super.viewDidLoad()
...

let first = favorite["banks"]![0]
favorite["favorite"]?.append(first)

}

在 TableView 中幾個重要的 function 輸入以下程式碼

// sevtion 數量
override func numberOfSections(in tableView: UITableView) -> Int {
return favorite.count
}
// section 中的 row 數量
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorite[keys[section]]!.count
}
// section 顯示的文字
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return keys[section]
}
// cell 中顯示的文字
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
var content = cell.defaultContentConfiguration()
content.text = favorite[keys[indexPath.section]]![indexPath.row]
cell.contentConfiguration = content

return cell
}

第三步

對 Bar Button Item 拉 @IBOutlet (sortBarButton)與 @IBAction (didtapsort)

並在 didtapsort() 中輸入以下程式碼

@IBOutlet weak var sortBarButton: UIBarButtonItem!

...

@IBAction func didtapsort(_ sender: UIBarButtonItem) {
if self.isEditing {
self.isEditing = false
sortBarButton.title = "Sort"
}else {
self.isEditing = true
sortBarButton.title = "Done"
}
}

每當點選 Bar Button Item 時, Bar Button Item 將會能夠切換 Cell 編輯模式,並且同步更新 Bar Button Item 顯示文字

第四步

新增以下程式碼讓 cell 能夠上下移動

override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
true
}

第五步

新增最重要的 TableView function

sourceIndexPath:你點選要移動的 cell 初始的 IndexPath

destinationIndexPath:也就是你移動後的 cell 終點的 IndexPath

移動 cell 流程:

  • 點選 cell 時:moveobject 抓取點選的 cell (sourceIndexPath.row)
  • 移除 favorite 中的銀行 cell (sourceIndexPath.row)
  • 移動停止後,將 moveobject 加入新的 IndexPath 位置 (destinationIndexPath.row)
  • 刪除第一個 favorite 中 favorite 的銀行,並且再次將順位第一的銀行放入 favorite 中
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

let moveobject = favorite["banks"]?[sourceIndexPath.row]
favorite["banks"]?.remove(at: sourceIndexPath.row)
favorite["banks"]?.insert(moveobject!, at: destinationIndexPath.row)

let first = favorite["banks"]![0]
favorite["favorite"]?.removeAll()
favorite["favorite"]?.append(first)

tableView.reloadData()

}

第六步

隱藏在切換 TableView 時顯示的刪除按鈕以及刪除按鈕空出位置

   // 隱藏 刪除按鈕
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .none
}
// 隱藏 刪除按鈕的空白位置
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
左:隱藏前 右:隱藏後

第七步

使用 userDefault 保存與讀取資 新的資料順序

let userDefault = UserDefaults.standard

override func viewDidLoad() {
super.viewDidLoad()

...

// 判斷是否有保存過資料
if (userDefault.array(forKey: "Banks") != nil) {
// 取代之前保存的順序
favorite["banks"] = userDefault.array(forKey: "Banks") as? [String]
favorite["favorite"]?.removeAll()
first = favorite["banks"]![0]
favorite["favorite"]?.append(first)
}


}


override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

...
// 保存初始資料到 userDefault
userDefault.set(favorite["banks"], forKey: "Banks")

}

以上程式碼在 viewDidload 中會先判斷是否有保存過 資料,若有會將上一次保存資料的順序進行同步,以上就是我所使用到的程式碼以及步驟,若有錯誤歡迎討論

GitHub

參考資料

--

--