如何在 swift 中使用委托和协议编辑和传回单元格数据

How to edit and pass back cell data with delegate and protocol in swift

我有两个 VC,第一个是 tableView,第二个是 detailedView VC,您可以在其中向 tableView 添加新项目。 我已经实现了使用 segues 向前传递数据(从第一个 VC 中的加号按钮)并在向 tableView 添加新项目时使用委托和协议向后传递数据(在第二个 VC 上点击保存按钮时触发) ).

我从原型单元格添加了一个 segue 到第二个 VC(详细视图),我还设法在第一个 VC 中测试了哪个 segue 被触发,即:添加新项目或转到该项目的详细视图。我面临的问题是,第二个 VC 中的保存按钮不再起作用(取消按钮也一样),我希望能够编辑第二个 VC 中的文本字段并点击保存按钮将编辑的项目保存回第一个。 我找到了一种使用 unwind segues 来实现它的方法,但是我想知道如何使用 delegate 来实现它?

我的第一个VC代码:

class ThingsTableViewController: UITableViewController, CanReceive {

    var myThings = [Thing]()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myThings.count
    }

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

        cell.textLabel?.text = myThings[indexPath.row].name
        cell.detailTextLabel?.text = myThings[indexPath.row].type

        return cell
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if segue.identifier == "addNewThing" {

            let secondVC = segue.destination as! UINavigationController

            let ThingsViewController = secondVC.topViewController as! ThingsViewController

            ThingsViewController.delegate = self

        } else if segue.identifier == "showDetail" {

            guard let thingDetailViewController = segue.destination as? ThingsViewController else {fatalError("Unknown Destination")}

            guard let selectedCell = sender as? UITableViewCell else {
                fatalError("Unexpected sender: \(sender)")
            }

            guard let indexPath = tableView.indexPath(for: selectedCell) else {
                fatalError("The selected cell is not being displayed by the table")
            }

            let selectedThing = myThings[indexPath.row]
            thingDetailViewController.thing = selectedThing

        }
    }

    func dataReceived(data: Thing) {

        if let selectedIndexPath = tableView.indexPathForSelectedRow {

        myThings[selectedIndexPath.row] = data
        tableView.reloadRows(at: [selectedIndexPath], with: .none)

        } else {

        myThings.append(data)
        tableView.reloadData()



    }

}

第二个 vc 中的代码如下所示:

 protocol CanReceive {

func dataReceived(data: Thing)

    }

}

class ThingsViewController: UIViewController, UITextFieldDelegate {

    var delegate : CanReceive?
    var thing : Thing?

    @IBOutlet weak var thingNameTextField: UITextField!

    @IBOutlet weak var thingTypeTextfield: UITextField!

    @IBAction func saveThingButton(_ sender: UIBarButtonItem) {
        let newThing = Thing(name: thingNameTextField.text!, type: thingTypeTextfield.text!)

        delegate?.dataReceived(data: newThing)

        self.dismiss(animated: true, completion: nil)
        self.navigationController?.popViewController(animated: true)

    }

    @IBAction func cancelButton(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true, completion: nil)
        self.navigationController?.popViewController(animated: true) 
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        thingNameTextField.delegate = self

        updateSaveButtonState()

        if let thing = thing {
            navigationItem.title = thing.name
            thingNameTextField.text = thing.name
            thingTypeTextfield.text = thing.type

        }
    }

    // MARK: UITextField Delegate

    // get triggered when the user hit the return key on the keyboard
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        thingNameTextField.resignFirstResponder()
        self.navigationItem.rightBarButtonItem?.isEnabled = true
        return true
    }

    //gives chance to read info in text field and do something with it
    func textFieldDidEndEditing(_ textField: UITextField) {
        updateSaveButtonState()
        navigationItem.title = thingNameTextField.text
    }

    func updateSaveButtonState() {

        let text = thingNameTextField.text
        self.navigationItem.rightBarButtonItem?.isEnabled = !text!.isEmpty
    }

}

ThingsViewControllerclass中,请用weak var

定义delegate
weak var delegate: CanReceive?

又发现一个问题,

看起来您的实例名称和 class 名称相同,请更新实例名称,

if segue.identifier == "addNewThing" {
    let secondVC = segue.destination as! UINavigationController
    let thingsVC = secondVC.topViewController as! ThingsViewController
    thingsVC.delegate = self 
} else if segue.identifier == "showDetail" { 
    guard let thingDetailViewController = segue.destination as? 
    ThingsViewController else {fatalError("Unknown Destination")}

        guard let selectedCell = sender as? UITableViewCell else {
            fatalError("Unexpected sender: \(sender)")
        }

        guard let indexPath = tableView.indexPath(for: selectedCell) else {
            fatalError("The selected cell is not being displayed by the table")
        }

        let selectedThing = myThings[indexPath.row]
        thingDetailViewController.thing = selectedThing
        thingDetailViewController.delegate = self
}

你的 tableView.reloadData() 应该会在 main queue

func dataReceived(data: Thing) {
    myThings.append(data)
    DispatchQueue.main.async {
        tableView.reloadData()
    }
}

声明接收数据的协议。

protocol ViewControllerDelegate: class {
func didTapButton(with data: Int)
}

在您要发送数据的地方声明一个协议委托

class SecondVC: UIViewController {

weak var delegate: ViewControllerDelegate?

@IBAction func buttonPressed(_ sender: UIButton) {
    delegate?.didTapButton(with: sender.tag)
}

}

确认要接收数据的协议,并让委托给自己。

class FirstVC : UIViewController,ViewControllerDelegate {


override func viewDidLoad() {
    super.viewDidLoad()

}

func gotoSecond() {
    let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "identifier") as! SecondVC
    vc.delegate = self
    self.navigationController?.pushViewController(vc, animated: true)
}

func didTapButton(with data: Int) {
    print(data)
}


}

您正在为 segue 的标识符为 addNewThing 的情况设置 delegate,但是标识符为 showDetail 的情况呢?

如果 segue 的标识符是 showDetail

,则设置 segue 的目的地 delegate
if segue.identifier == "addNewThing" {
    ...
} else if segue.identifier == "showDetail" {
    ...
    thingDetailViewController.delegate = self
    ...
}

然后当你需要关闭 ViewController embed in navigation controller 时,只需关闭它然后关闭导航控制器