Is there something like prepareForSegue when going back with UINavigationController? - ios

I have an array in my ViewOne, which I pass to ViewTwo with the prepareForSegue function.
When I delete data of my array in ViewTwo and go back with the back button on the NavigationController or by swiping to right, the array in ViewOne has still all the data and don't know that I deleted something of it.
Is there any solution for this problem?
ViewOne:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "submitSegue") {
let shoppingCart = segue.destinationViewController as! ShoppinCartScreen;
if(artikel != nil) {
shoppingCart.alleArtikel = alleArtikel
print("Test: \(artikel?.artikelnummer)")
}
}
}
ViewTwo:
var alleArtikel = [Artikel]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return alleArtikel.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ShoppingCartScreenCell", forIndexPath: indexPath) as!ShoppingCartScreenCell
let eintrag = alleArtikel[indexPath.row]
cell.previewImage.image = eintrag.foto
cell.artikelNummer.text = eintrag.artikelnummer
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if(editingStyle == .Delete) {
alleArtikel.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
I have a main View (ViewOne) and a TableView(ViewTwo).

In Swift, arrays are value types not reference types (see Apple Developer Blog entry), so when you 'pass' it to view2 you are really just making a copy. View2 modifies the copy but the original remains unchanged.

Instead of using a prepareForSegue method when going back, which to my knowledge is not possible, just run the viewWillDisappear function. Basically this is the viewDidLoad function for when the view disappears. It will run when you are performing a segue when you are leaving the view.
example:
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
print("the view is hidden")
}

Related

How to add data to the tableView and reload?

I'm trying to add a cell to a tableView that's in viewController by sending data via a segue from another viewController.
class FavoritesViewController: UIViewController {
var shops = [
"hello world",
"hello world",
"hello world"
]
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
table.reloadData()
}
}
//protocol FavoritesDelegate: class {
// func add(_ shopName: String)
//}
extension FavoritesViewController: UITableViewDelegate, UITableViewDataSource{
func add(_ shopName: String) {
print(shopName)
shops.append(shopName)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shops.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath)
cell.textLabel?.text = shops[indexPath.row]
return cell
}
// define the action. In this case "delete"
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
// do the actual deleting
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.beginUpdates()
shops.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
}
And here's the function call in the other viewController (prepare):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
favoritesDestinationVC = segue.destination as! FavoritesViewController
favoritesDestinationVC.add(shopName!)
}
I know what's causing the error (favoritesDestinationVC creates a new instance where tableView is nil), but I don't know how to solve it. Any ideas on how I could add an entry to the tableView that way (and updating the table afterwards) without my app crashing?
Make your shops var public and then in the segue prepare callback use it directly. Check a variable first whether it has a valid value other than the nil, if it does then proceed to avoid crashes. You can do it using an if statement to verify as you can see in the sample code. Or you can use optionals to avoid crashes. See the code below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is FavoritesViewController {
let destinationVC = segue.destination as? FavoritesViewController // Use optionals which has ? (question mark)
// To avoid crashes check if shopName has a valid value other than nil
if let newShopName = shopName {
// It is possible that the "shopName" has a nil value when the program reaches here.
// That's why we will use the verified "newShopName" instead of "shopName" itself to avoid any crash.
destinationVC?.shops.append(newShopName) // What I recommend, if not convenient for you just delete this line,
// destinationVC?.add(newShopName) // after deleting the recommended line, uncomment this line for your convenience.
}
}
}

Segue not firing from custom tableView cell

I'm experiencing what I'm sure is a beginner's mistake. I have a TableViewController that should segue to a second TableViewController upon selection of a custom cell.
I've segued to a UIViewController from a custom cell in the past but I can't figure out for the life of me why this segue isn't firing. Here's the code for the entire controller that should be firing the segue. Thanks in advance!
import UIKit
class SourcesViewController: UITableViewController {
var sources: [Source] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = false
self.navigationItem .setHidesBackButton(true, animated: false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sources.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell_01", for: indexPath) as? SourceCell
else {
return tableView.dequeueReusableCell(withIdentifier: "cell_01", for: indexPath)
}
let currentSource = sources[indexPath.row]
cell.sourceLabel.text = currentSource.name
return cell
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
let sourceToSend = sources[indexPath.row]
if let destination = segue.destination as? ArticlesViewController {
destination.source = sourceToSend
}
}
}
}
Your segue doesn't get called because you are not invoking it. UIViewController has perfromSegue method that you should use for that purpose.
From what I see you would want to perform the segue after tapping the cell. To achieve this add UITableViewDelegate method and perform segue from there:
/// - SeeAlso: UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "your_identifier_from_the_storyboard", sender: nil)
}

swift: tableview returning animation

I want to create tableview like on image(https://www.raizlabs.com/dev/wp-content/uploads/sites/10/2016/05/Default-Deselection.gif) In this table view I see animation of selected row after returning to the first controller with table view.
But when I create default tableview in my project animation of selected row after returning to the first controller not show. How to fix it?
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(format: "Cell%d", indexPath.row), for: indexPath)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.destination is ViewController) {
(segue.destination as? ViewController)?.index = (self.tableView.indexPathForSelectedRow?.row)!
}
}
}
If you want the selected row color to fade out after navigating back(pan the screen edge), you can use this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}

array index out of bounds when UITableViewCell tapped, with and without a didselectrowatindex method

Like Title
More information - I am populating my view controller with
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showCalendars" {
// Setup new view controller
print("happening")
let vc = segue.destinationViewController as! CalendarSelectionViewController
for item in self.approvedCalendars {
vc.sentCalendars.append(item)
}
vc.tableView.reloadData()
vc.calendarDelegate = self
}
}
and my view controller code looks like :
import UIKit
import EventKit
class CalendarSelectionViewController: UITableViewController {
var sentCalendars: [EKCalendar]! = []
var calendarDelegate: selectCalendarDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Calendars"
//tableView.tableFooterView = UIView()
tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sentCalendars.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("calendarCell", forIndexPath: indexPath)
cell.textLabel?.text = sentCalendars[indexPath.row].title
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 60
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
whenever i click on any uitableview cell, my application crashes and I receive "fatal error: Array index out of range".
I've tried printing out the indexPath.row in the didSelectRowAtIndexPath method and it prints the correct index, so why is this crashing? Is there a memory leak somewhere and the tableview is not showing the proper information? Thanks for your help.
Investigated my storyboard, I had an unwind segue that wasn't being used and was causing a fatal error: array index out of bounds. Thanks for the help!

Nil data is passed from tableview detail disclosure with prepareForSegue

I need to pass a string with prepareForSegue from a tableView disclosure link. Here is the prepareForSegue code I use. This is the code I found in several cases when other users needed to do the same thing, but in my case it doesn't work. Problem is with indexPathForSelectedRow() that is nil:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "alertsDetail" {
let detailAlertViewController = segue.destinationViewController as DetailAlertViewController
println(alertTable.indexPathForSelectedRow()) // NIL
if let indexPath = alertTable.indexPathForSelectedRow() {
let entryToPass = unpublishedPhotosObjectId[indexPath.row]
detailAlertViewController.entryId = entryToPass
}
}
}
I named the tableView "alertTable" and not self because the tableview is not a view controller, just a view.
In case needed, here is tableview code:
#IBOutlet weak var alertTable: UITableView!
/////////////TableView - START
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return unpublishedPhotos.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 170
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
tableView.separatorColor = UIColor.clearColor()
let cell = tableView.dequeueReusableCellWithIdentifier("adminAlertsCell") as? adminAlertsCell
var data = unpublishedPhotos[indexPath.row].getData()
let image = UIImage(data: data)
cell!.myImageInCell.image = image
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// Add access to cell
let cell = tableView.cellForRowAtIndexPath(indexPath) as? adminAlertsCell
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
println(cell!.banSwitch.on)
}
}
/////////////TableView - END
You should make the segue from the controller instead of directly from the detail disclosure button. Implement the method,
tableView:accessoryButtonTappedForRowWithIndexPath: and call performSegueWithIdentifier:sender: from inside that method. Pass the in-depth as the sender argument so you have access to that in prepareForSegue,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let indexPath = sender as NSIndexPath
if segue.identifier == "alertsDetail" {
let detailAlertViewController = segue.destinationViewController as DetailAlertViewController
let entryToPass = unpublishedPhotosObjectId[indexPath.row]
detailAlertViewController.entryId = entryToPass
}
}
Your issue is stated in the comments to your question, and there are several solutions. Perhaps the quickest since you only have one section, is to assign the indexPath.row to the tag of the cell. Then in your prepare for segue, the sender will be your cell. You can get the value out of it. So in your cellForRow method before your return your cell:
cell!.tag = indexPath.row
Then in your prepareForSegue:
let cell = sender as? adminAlertsCell
println(cell!.tag)
Here I am just printing it, but you can use it to pass the row.

Resources