iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0
Mobile Development

想知道自己iOS具現化系能力有多強嗎?實作幾個App就知道了系列 第 5

Day5-實作To-Do-List之修改(頁面傳值/closure傳值)

話不多說,直接上圖,實作修改功能:
修改功能
如圖所示,我想要把下午四點的吃點心時間改成去慢跑。

接下來分析我們的需求。

  • 1.需要往左滑帶出修改按鈕
  • 2.點選修改按鈕之後跳轉到修改頁面
  • 3.將原先的值傳送到修改頁面並且顯示
  • 4.實作將值傳回到首頁的方法,並且加進去儲存按鈕
  • 5.點選儲存按鈕呼叫第四點實作的方法,並且頁面轉場(移除目前頁面)
  • 6.更新剛剛點選修改按鈕的cell內容,只更新單行(更新指定cell)

首先先把修改按鈕給做出來,昨天已經講過方法,這邊直接上程式碼:

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傳值的細節,我會在明天詳細講如何實作。


上一篇
Day4-實作To-Do-List之刪除、插入
下一篇
Day6-實作To-Do-List之修改功能(Closure傳值)、分享功能(UIActivityViewController))
系列文
想知道自己iOS具現化系能力有多強嗎?實作幾個App就知道了30

1 則留言

0
alan5899f
iT邦新手 5 級 ‧ 2021-07-08 03:39:46

請問一下 你在ModifyViewController的listData屬性是 怎麼設定
我在destinationVC.listData = oldValue 他會說 沒有辦法回傳值 怎麼會這樣

你好,可以的話,方便把錯誤訊息貼上來嗎?

alan5899f iT邦新手 5 級 ‧ 2021-07-08 13:17:18 檢舉

可以執行了 謝謝!!

我要留言

立即登入留言