I have two VC, the first is a tableView, the second is the detailedView VC where you can add a new item to the tableView. I have implemented passing data forward with segues (from plus button in the first VC) and backwards with delegate and protocol when adding a new item to the tableView (triggered when tapping a save button on the second VC).
I added a segue from the prototype cell to the second VC (detailed view), I have also managed to test in the first VC which segue is triggered, ie: add new item or go to the detailedView of that item. the problem I'm facing, the save button in the second VC no longer works (and the cancel button also), I want to be able to edit the text fields in the second VC and hit the save button to save the edited item back in the first one. I found a way to implement it with unwind segues, however I would like to know how to do it with delegate ?
My first VC code:
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()
}
}
the code in the second vc look like:
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
}
}
In ThingsViewController
class, please define delegate with weak var
weak var delegate: CanReceive?
One more issue is observed,
Looks like your instance name and class name are same, please update the instance name,
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
}
Your tableView.reloadData()
should happen in main queue
func dataReceived(data: Thing) {
myThings.append(data)
DispatchQueue.main.async {
tableView.reloadData()
}
}
You're setting delegate
for case that segue's identifier is addNewThing
, but what about case that identifier is showDetail
?
Set delegate
of segue's destination for case that segue's identifier is showDetail
if segue.identifier == "addNewThing" {
...
} else if segue.identifier == "showDetail" {
...
thingDetailViewController.delegate = self
...
}
Then when you need to dismiss ViewController embed in navigation controller, just dismiss it and then dismiss navigation controller
Declare a protocol for receiving data.
protocol ViewControllerDelegate: class {
func didTapButton(with data: Int)
}
declare a delegate of protocol where you are sending the data
class SecondVC: UIViewController {
weak var delegate: ViewControllerDelegate?
@IBAction func buttonPressed(_ sender: UIButton) {
delegate?.didTapButton(with: sender.tag)
}
}
confirm to the protocol where you want to receive the data and make the delegate to self.
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)
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.