話不多說,直接上圖,實作修改功能:
如圖所示,我想要把下午四點的吃點心時間改成去慢跑。
接下來分析我們的需求。
首先先把修改按鈕給做出來,昨天已經講過方法,這邊直接上程式碼:
let modifyAction = UIContextualAction(style: .normal, title: "修改") { (action, sourceView, complete) in
//先把目前cell內的值存到oldValue以及這個屬性
let oldValue = self.listDatas[indexPath.row]
//我在class內宣告了一個indexPath的屬性要存選取時的索引,讓我整個class都可以使用這個屬性
self.indexPath = indexPath
//實例化一個視圖控制器
let destinationVC = self.storyboard?.instantiateViewController(identifier: "ModifyViewController") as! ModifyViewController
//將目前的值傳送給ModifyViewController的listData屬性
destinationVC.listData = oldValue
//我讓ModifyViewController推出時的轉場動畫是show,也就是畫面由右往左插入,並且NavigationController會附贈一個左上角的Back按鈕,內建返回上一頁的功能。
self.show(destinationVC, sender: self)
complete(true)
}
modifyAction.image = UIImage(systemName: "pencil.and.ellipsis.rectangle")
modifyAction.backgroundColor = UIColor.systemPurple
//記得加入剛剛的修改動作進去
let trailingSwipConfiguration = UISwipeActionsConfiguration(actions: [deleteAction,insertAction,modifyAction])
現在你已經完成了點選修改按鈕,頁面可以轉場到修改頁面了,或許你會想問第一天設置的segue是為什麼,其實剛剛使用程式碼實例化修改頁面並且轉場,跟使用segue是相同的作用,但是使用segue會在一開始就讓你的頁面上方產生NavigationBar,可以在這個時候就開始設置UI介面,至於之後使用程式碼實例化修改頁面,優先度是以程式碼優先的,所以並不會觸發segue,你可以想成在這個實作上,只是為了讓頁面更好設置而使用segue。
你會發現目前值已經帶過去了,但是第一天設置的UITextField、UITextView都沒有文字,因為雖然得到值,但是還沒有寫讓值顯示的程式碼,讓我們來把它做出來吧:
override func viewDidLoad() {
super.viewDidLoad()
modifyTV.text = listData.title
modifyTF.text = listData.subtitle
}
以上程式碼是在生命週期內實作,viewDidLoad(),是在頁面一開始載入記憶體時就會呼叫的方法,我使用這個方法,讓我的文字在一開始就可以顯示,但是要注意的是viewDidLoad()只會在生命週期出現一次
,這點滿重要的。
現在已經可以正常顯示文字了,但是很烙賽的一點是,點Save按鈕,並沒有什麼事情發生,因為按鈕內部沒有任何實作,接下來的程式碼比較複雜,需要使用到closure傳值
以及optional解包
,關於這部分的實作,我會分開到明天(Part 2)講,詳細知識可以參考我的好友Alvin
的文章,他有非常詳細且生動的解說,我盡量將重點放在實作上,首先在ModifyViewController
內宣告一個閉包的型別是((ListModel) -> Void)?
:
var passingToATVCClosure: ((ListModel) -> Void)?
接著一樣在ModifyViewController
內,我實作一個Save按鈕點下後會呼叫的func:
func savingModify()
{
// if let XXX = YYY{},這是將optional解包的做法
if let text0 = modifyTV.text, let text1 = modifyTF.text{
// 將目前的這筆資料暫存到區域常數(data)內
let data = ListModel(title: text0, subtitle: text1)
//使用closure傳值,在ModifyViewController內宣告的屬性,丟入data這個型別是`ListModel`的常數,你可能會對於這段程式碼感到困惑,我會在明天詳細講解。
passingToATVCClosure?(data)
//轉場回上個頁面,因為我的RootViewController是上個頁面,因此我使用popToViewController(animated:),還有許多種不同的轉場,有需要的同學可以自行搜尋NavigationController轉場
navigationController?.popToRootViewController(animated: true)
}
}
接下來將savingModify()加進去按鈕的func:
@IBAction func savingBtn(_ sender: UIBarButtonItem) {
savingModify()
}
到目前為止,你已經實作完1-5點了,剩下更新表格視圖的指定行數,還記得剛剛要將值傳送到第二頁面時,我在class內宣告了一個indexPath變數用來儲存目前的索引嗎?是時候用上它了,在生命週期viewWillAppear(_:)
加入下列程式碼:
override func viewWillAppear(_ animated: Bool) {
//這邊要將optional解包,否則當載入到這個生命週期時,如果發現indexPath是nil,則會閃退
if let indexPath = self.indexPath{
//這行程式碼是只更新特定行數的表格內容,因為在tableView(_:trailingSwipeActionsConfigurationForRowAt:)的索引值無法直接指定到這邊,所以只能先存在class內宣告的indexPath內,再在這邊使用
self.toDoListTableView.reloadRows(at: [indexPath], with: .top)
}
}
viewWillAppear(_:)
是只要每次畫面將要出現前都會被呼叫,所以我將資料更新的實作放在這邊,現在你的資料可以修改但是還沒辦法回傳顯示到表格上,因為還少幾行關鍵的程式碼,關於closure傳值的細節,我會在明天詳細講如何實作。
請問一下 你在ModifyViewController的listData屬性是 怎麼設定
我在destinationVC.listData = oldValue 他會說 沒有辦法回傳值 怎麼會這樣
你好,可以的話,方便把錯誤訊息貼上來嗎?
可以執行了 謝謝!!