Segue saves data on back bar button - ios

I've a segue on TableViewCell click, and a save button on another ViewController, it works fine on save button, but it also changes TableViewCell when i click on back bar button. How can i discard changes? There are NoteTableViewController and NoteViewController
TableViewController methods
tableView cellRowAt method
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "NoteTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! NoteTableViewCell
let note = notes[indexPath.row]
cell.noteLabel.text = note.note
return cell
}
TableViewController prepare for segue method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDetail" {
let noteDetailViewController = segue.destination as! NoteViewController
if let selectedNoteCell = sender as? NoteTableViewCell {
let indexPath = tableView.indexPath(for: selectedNoteCell)!
let selectedNote = notes[indexPath.row]
noteDetailViewController.note = selectedNote
noteDetailViewController.oldNote = someNote
}
}
}
This method is for saving data, it connected to saveButton of NoteViewController
#IBAction func unwindToNoteList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? NoteViewController {
let note = sourceViewController.note
if note?.note != nil {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
notes[selectedIndexPath.row] = note!
tableView.reloadRows(at: [selectedIndexPath], with: .none)
} else {
let newIndexPath = NSIndexPath(row: notes.count, section: 0)
notes.append(note!)
tableView.insertRows(at: [newIndexPath as IndexPath], with: .top)
}
saveNotes()
}
}
}
NoteViewController methods
prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if saveButton === sender as? UIBarButtonItem {
if let noteText = noteTextView.text {
note?.note = noteText
}
note?.toDate = remindDate
note?.image = photoImageView.image
} else {
note = oldNote
}
}

Problem solved, by implementation NSObject copy() method. And sending a copy to NoteViewController.

Related

Swift UISwipeActionsConfiguration and segue

I added leadingSwipeAction "Edit" button. However, when i press "Edit" button in simulator, app crashes and shows "Thread 1: signal SIGBART" in **prepare(for:sender:)**method.
I saw similar questions, but their solutions did't help. I'm newbie and can't understand where is the problem.
`//my edit button code`
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let edit = UIContextualAction(style: .normal, title: "Edit") { [self] (contextualAction, view, actionPrformed: (Bool) -> Void) in
//TODO:
performSegue(withIdentifier: "EditItem", sender: self)
actionPrformed(true)
}
return UISwipeActionsConfiguration(actions: [edit])
}
// segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddItem" {
let controller = segue.destination as! AddAndEditItemViewController
controller.delegate = self
}
else if segue.identifier == "EditItem" {
let controller = segue.destination as! AddAndEditItemViewController
controller.delegate = self
if let indexPath = tableView.indexPath(
for: sender as! UITableViewCell) { //erorr shows here
controller.itemToEdit = items[indexPath.row]
}
}
}
self here
performSegue(withIdentifier: "EditItem", sender: self)
is the vc instance itself not the cell , you need to pass indexPath.row
performSegue(withIdentifier: "EditItem", sender: indexPath.row)
Then
let index = sender as! Int
controller.itemToEdit = items[index]
You can also use
guard let index = tableView.indexPathForSelectedRow?.row else { return }

Push segue from Cell doesn't work after click

I have been reading around on various ways to perform segues. I want a push segue from a cell click. I have the segue in the storyboard, so I am not using the didSelectForRow function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let selectedRow = self.tableView.indexPathForSelectedRow as? Int else { return }
if segue.identifier == "detail", let vc = segue.destination as? DetailViewController {
vc.playerImage = UIImageView(image: UIImage(named: "userIcon"))
vc.currentRankingLabel.text = String(players[selectedRow].ranking)
vc.scoreLabel.text = String("\(players[selectedRow].wins) - \(players[selectedRow].losses)")
}
}
This is what I have got at the moment. It compiles, but the cell will not react to the click!
indexPathForSelectedRow is never Int, it's IndexPath?. Delete the conditional downcast and get the row from its row property
guard let selectedPath = self.tableView.indexPathForSelectedRow else { return }
let selectedRow = selectedPath.row
if segue.identifier == "detail", let vc = segue.destination as? DetailViewController {
vc.playerImage = UIImageView(image: UIImage(named: "userIcon"))
vc.currentRankingLabel.text = String(players[selectedRow].ranking)
vc.scoreLabel.text = String("\(players[selectedRow].wins) - \(players[selectedRow].losses)")
}
However if the segue is connected to the cell I recommend to use the sender parameter which represents the cell
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail", let vc = segue.destination as? DetailViewController {
let selectedPath = self.tableView.indexPath(for: sender as! UITableViewCell)!
let selectedRow = selectedPath.row
vc.playerImage = UIImageView(image: UIImage(named: "userIcon"))
vc.currentRankingLabel.text = String(players[selectedRow].ranking)
vc.scoreLabel.text = String("\(players[selectedRow].wins) - \(players[selectedRow].losses)")
}
}
Important note:
Be aware that the outlets in the destination controller are not connected in prepare(for so nothing will be displayed or the code even crashes.
I think you need call performSegue on cell selection, like below
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "detail", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let selectedPath = sender as? IndexPath, let selectedRow = selectedPath.row, let player = players[selectedRow] else {
return
}
if segue.identifier == "detail", let vc = segue.destination as? DetailViewController {
vc.playerImage = UIImageView(image: UIImage(named: "userIcon"))
vc.currentRankingLabel.text = String(player.ranking)
vc.scoreLabel.text = String("\(player.wins) - \(player.losses)")
}
}

navigationItem.title from UITableViewCell

Navigation Bar title is not appearing according to the Selected Cell.
I have the Navigation View Controller inside a Container View.
The Container View is in ViewController.
ViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "embedseg" {
guard let splitViewController = segue.destination as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
//let detailViewController = splitViewController.viewControllers.last as? DetailViewController
let rightNavController = splitViewController.viewControllers.last as? UINavigationController,
let detailViewController = rightNavController.topViewController as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstMonster
masterViewController.delegate = detailViewController
detailViewController.navigationItem.leftItemsSupplementBackButton = true
detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
//detailViewController.navigationItem.title = "Hello" <-- tested this, it worked.
//detailViewController.navigationItem.title = cell.textLabel?.text <-- this didn't work.
//detailViewController.navigationItem.title = monster.name <-- and this didn't work.
}
}
MasterViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let monster = monsters[indexPath.row]
cell.textLabel?.text = monster.name
return cell
}
When you select the cell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! YourCellClassName
self.performSegue(withIdentifier: "embedseg", sender:cell.textLabel?.text)
}
in prepareforSegue
let cellText = sender as! String
detailViewController.navigationItem.title = cellText
This happens because monster and cell are defined inside
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
but not inside
override func prepare(for segue: UIStoryboardSegue, sender: Any?){}
If you have set your segues using the storyboard you can do something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let cell = sender as? UITableViewCell {
detailViewController.navigationItem.title = cell.textLabel?.text
}
}
or something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let cell = sender as? UITableViewCell {
let indexPath = tableView.indexPath(for: cell)
detailViewController.navigationItem.title = monsters[indexPath.row].name
}
}
In the first case you cast "sender" as the cell so you can access it's values, in the second case you cast "sender" as the cell, you find what is it's indexPath and then you use the indexPath to find the correct "monster" using you array monsters

TableViewCell to ViewController - Swift

I tried many ways but still can't go to another ViewController. This is my storyboard.
and This is my code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell {
listItems.sort() { $0.seq > $1.seq }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
} else {
return HistoryMainTableViewCell()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let productHistoryPage = segue.destination as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
Try this code:(Untested Code)
Note: Below code will trigger prepare segue. When, tableViewCell selected.Let me know the code works...
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
// let indexPath = myTableView!.indexPathForSelectedRow
// let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
//sendSelectedData = (currentCell.textLabel?.text)! as String as NSString
performSegue(withIdentifier: "vcProductHistory", sender: self)
}
Based on your answer to my initial comment: dragging the line from initial vc to destination vc tells your vc about the segue, preparing for segue tells your vc what do when the segue is triggered, but you still need to let your vc know when it should perform the segue. try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell else { return HistoryMainTableViewCell() }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
guard let productHistoryPage = segue.destination as? HistoryProductViewController, let indexPath = sender as? IndexPath else { return }
let historyArray = listItems[indexPath.row]
productHistoryPage.history = historyArray
}
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
performSegue(withIdentifier: "vcProductHistory", sender: indexPath)
}
I think this is a little bit neater using the guard syntax. Also, it uses sender as the indexPath, not using tableView.indexPathForSelectedRow.
Also, I noticed you have this in your cellForRowAtIndexPath:
listItems.sort() { $0.seq > $1.seq }
Your sorting your data source in cellForRowAtIndexPath. You can't do that there because that function gets called for each row individually. You need to do the sorting before that is called, somewhere like viewWillAppear. Also, to sort an array in place you need to remove the () after sort. try this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listItems.sort { $0.0.seq > $0.1.seq }
tableView.reloadData()
}
I think you should connect to UINavigationController , not to HistoryProductionViewController
Because your MainViewController's segue connected to Navigation Controller
I guess in func prepareForSegue segue.identifier == "vcProductHistory" it works but, if let productHistoryPage = segue.destination as? HistoryProductViewController not works
because your destination ViewController's class is NavigationController
you should call
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let naviVC = segue.destination as? UINavigationController {
if let productHistoryPage = naviVC.topViewController as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
}
Your solution - as you described it - should work.
The most work here can be done directly in the Storyboard.
Select the prototype cell in the TableView, drag the Segue to the NavigationController and select presentModally.
This should work without further magic.
When selecting the TableViewCell the NavigationController should be presented. There is also no code needed at this point (except for the population of the TableView).
If not - you maybe misconfigured your TableView by setting the selection to No Selection:
or you set User Interaction Enabled to false somewhere.

How to get custom cell label when segue?

I'm trying to get the name of the selected row in the custom cell. How I can do that in Swift?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var selectedCell = CustomCell()
selectedCell = tableView(tbl, cellForRowAtIndexPath: <#NSIndexPath#>)
if(segue.identifier == "next") {
var next = (segue.destinationViewController as! EngineViewController)
next.titleSub = selectedCell.cell.lblBrand.text
}
}
I believe if you are just selecting a cell, the cell should be the sender. Try this
if let selectedCell = sender as? CustomCell {
if(segue.identifier == "next") {
var next = (segue.destinationViewController as! EngineViewController)
next.titleSub = selectedCell.cell.lblBrand.text
}
}
You could attempt to override didSelectRowAtIndexPath to get your cell.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as! CustomCell
cell.yourLabel.textYouWant
let yourViewController = storyboard?.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
navigationController?.pushViewController(yourViewController, animated: true)
}

Resources