This is my first question here. I have TableViewController and I want to use two segues from one raw. One segue should work when you tapped a row, and the second is action from this row. Every of them need to show different ViewControllers and I don't understand how can I do that because I can't to create two segues from one row. The problem is that both of cases need to call prepare function and it called only with segue, and it does not called when I use performSegue.
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let editAction = UIContextualAction(style: .normal, title: "Add"){(_,_, completionHandler) in
self.performSegue(withIdentifier: "editQuiz", sender: self)
tableView.reloadData()
}
let deleteAction = UIContextualAction(style: .destructive, title: "Delete"){(_,_, completionHandler) in
CoreDataManager.shared.deleteSomeQuizData(data: CoreDataManager.shared.quizzes[indexPath.row], indexNumber: indexPath.row)
self.navigationItem.title = "\(CoreDataManager.shared.quizzes.count) quizzes"
tableView.reloadData()
}
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let indexPath = tableView.indexPathForSelectedRow else {return}
print(segue.source)
if segue.identifier == "editQuiz" {
guard let destination = segue.destination as? AddWordsViewController else{return}
}
if segue.identifier == "showQuiz" {
guard let destination = segue.destination as? QuizViewController else{return}
destination.from = Int(CoreDataManager.shared.quizzes[indexPath.row].from)
destination.to = Int(CoreDataManager.shared.quizzes[indexPath.row].to)
}
}
prepare(for segue:) works only when it called from row segue to another ViewController and it didn't called with performSegue. Also if I create both of segue from TableViewController to ViewControllers and don't call performSegue, transition doesn't work.
All of the segues identifiers set correctly.
Even if I try
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showQuiz", sender: self)
}
prepare(for segue:) is calling, but in
let editAction = UIContextualAction(style: .normal, title: "Add"){(_,_, completionHandler) in
self.performSegue(withIdentifier: "editQuiz", sender: self)
tableView.reloadData()
}
it does not calling.
It looks like you have almost everything setup correctly, except...
In your prepare for segue
code, the first thing you do is check for the selected row:
guard let indexPath = tableView.indexPathForSelectedRow else {return}
If you call performSegue
from your "Edit" action, the table view will NOT HAVE a selected row.
A bit tough for me to test because I don't have all of your data management and destination controllers, but this should fix the issue (if you have your segues setup correctly in Storyboard):
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let editAction = UIContextualAction(style: .normal, title: "Add"){(_,_, completionHandler) in
// instead of passing self as sender
//self.performSegue(withIdentifier: "editQuiz", sender: self)
// pass the indexPath
self.performSegue(withIdentifier: "editQuiz", sender: indexPath)
// no need to reload data here
//tableView.reloadData()
}
let deleteAction = UIContextualAction(style: .destructive, title: "Delete"){(_,_, completionHandler) in
CoreDataManager.shared.deleteSomeQuizData(data: CoreDataManager.shared.quizzes[indexPath.row], indexNumber: indexPath.row)
self.navigationItem.title = "Title \(indexPath.row)" // "\(CoreDataManager.shared.quizzes.count) quizzes"
tableView.reloadData()
}
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editQuiz" {
// do something before segue to AddWordsViewController
if let indexPath = sender as? IndexPath {
print("editQuiz with IndexPath: \(indexPath)")
}
}
if segue.identifier == "showQuiz" {
guard let indexPath = tableView.indexPathForSelectedRow,
let destination = segue.destination as? QuizViewController
else {
// note: this will NOT Stop the segue
return
}
destination.from = Int(CoreDataManager.shared.quizzes[indexPath.row].from)
destination.to = Int(CoreDataManager.shared.quizzes[indexPath.row].to)
}
}
As I mentioned in my comment: "To give yourself the most control, don't use segues in that situation..."
To do that, delete the segues from your Storyboard.
Instead, use code like this to instantiate the "destination" view controllers as needed:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// instantiate QuizViewController
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "QuizViewController") as? QuizViewController {
vc.from = Int(CoreDataManager.shared.quizzes[indexPath.row].from)
vc.to = Int(CoreDataManager.shared.quizzes[indexPath.row].to)
self.navigationController?.pushViewController(vc, animated: true)
}
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let editAction = UIContextualAction(style: .normal, title: "Add"){(_,_, completionHandler) in
// instantiate AddWordsViewController
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "AddWordsViewController") as? AddWordsViewController {
// do something before showing AddWordsViewController
self.navigationController?.pushViewController(vc, animated: true)
}
}
let deleteAction = UIContextualAction(style: .destructive, title: "Delete"){(_,_, completionHandler) in
CoreDataManager.shared.deleteSomeQuizData(data: CoreDataManager.shared.quizzes[indexPath.row], indexNumber: indexPath.row)
self.navigationItem.title = "Title \(indexPath.row)" // "\(CoreDataManager.shared.quizzes.count) quizzes"
tableView.reloadData()
}
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}