mihi
mihi

Reputation: 47

How to update tableviewcell using back button?

I have this tableviewcontroller and detailviewcontroller which has a text field, when the user clicks the tableviewcell it goes to text field and the user could able to edit the content!! How should I update the table view with new content in the text field through back button?

let sectionTitles = ["ongoing","Done"]
var list = [String]()

class MasterViewController: UITableViewController {

    var detailViewController: DetailViewController? = nil
    var objects = sectionTitles.map({_ in return [Any]()})

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        navigationItem.leftBarButtonItem = editButtonItem

        let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
        navigationItem.rightBarButtonItem = addButton

        if let split = splitViewController {
            let controllers = split.viewControllers
            detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
        }
    }

    @objc
    func insertNewObject(_ sender: Any) {
        objects[0].insert("Task 1", at: 0)
        let indexPath = IndexPath(row: 0, section: 0)
        tableView.insertRows(at: [indexPath], with: .automatic)
    }

    // MARK: - Segues

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            if let indexPath = tableView.indexPathForSelectedRow {
                let object = objects[indexPath.section][indexPath.row] as! String
                let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
                controller.detailItem = object

                controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
                controller.navigationItem.leftItemsSupplementBackButton = true
            }
        }
    }
}

class DetailViewController: UIViewController,UINavigationControllerDelegate {

    @IBOutlet weak var detailDescription: UITextField!

    func configureView() {

        // Update the user interface for the detail item.
        if let detail = detailItem {

            if let label = detailDescription {
                label.text = detail.description
                list.append(label.text!)
            }
        }
    }
}

Upvotes: 0

Views: 263

Answers (3)

vadian
vadian

Reputation: 285260

I recommend to use a callback closure to update the model and reload the row. The callback is called in viewDidDisappear. The code in the closure checks if the values are different and updates the model and reloads the row if necessary.

  • In DetailViewController create a callback

    var callback : ((String)->())?
    
    • In viewDidDisappear call the callback (or maybe already in viewWillDisappear)

      override func viewDidDisappear(_ animated: Bool) {
          super.viewDidDisappear(animated)
          callback?(detailDescription.text!)
      }
      
  • In MasterViewController add the closure in prepare(for

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            if let indexPath = tableView.indexPathForSelectedRow {
                let object = objects[indexPath.section][indexPath.row] as! String
                let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
                controller.detailItem = object
                controller.callback = { newValue in 
                   if object != newValue {
                       self.objects[indexPath.section][indexPath.row] = newValue
                       self.tableView.reloadRows(at: [indexPath], with: .automatic)
                    }
                }
                controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
                controller.navigationItem.leftItemsSupplementBackButton = true
            }
        }
    }
    

Upvotes: 1

Mindhavok
Mindhavok

Reputation: 457

You could use the delegate pattern:

protocol DetailViewDelegate: class {
    func detailViewDidUpdate(from: String?, to: String?)
}

class DetailViewController : UIViewController {

    var detailItem: String?

    weak var delegate: DetailViewDelegate?

    @IBOutlet weak var textField: UITextField!


    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField.text = detailItem
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.delegate?.detailViewDidUpdate(from: detailItem, to: textField.text)
    }
}

Then in your MasterViewController:

extension MasterViewController: DetailViewDelegate {
    func detailViewDidUpdate(from: String?, to: String?) {
        guard let originalValue = from, let newValue = to else {
            return
        }

        if let indexPath = self.objects.index(of: originalValue) {
            self.objects[indexPath] = newValue
            // We assume here that there is only one section
            let newIndexPath = IndexPath(row: indexPath, section: 0)
            self.tableView.reloadRows(at: [newIndexPath], with: .automatic)
        }
    }
}

Don't forget to assign the DetailViewController's delegate variable in your MasterController prepare(for segue: UIStoryboardSegue, sender: Any?) method

Upvotes: 0

A.Munzer
A.Munzer

Reputation: 1990

When you tapped back button, viewWillAppear will be called, its one View Controller Lifecycle

for example

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.tableView.reloadData()
    }

Upvotes: 0

Related Questions