Swift 2 - prepareForSegue nil Value - ios

I am trying to send a link over another view when a select a row in UITableView.
When I select the row I can see the link is printed however, the value does not get to the function prepareForSegue.
It seems like the prepareForSegue function is called before the selection row in UITableView.
var videouserPath = ""
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
videouserPath = self.arrayUserVideo[indexPath.row][4]
print("tableview: ----\(videouserPath)")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("segue: ----\(videouserPath)")
if segue.identifier == "toUserVideo"
{
if let destinationVC = segue.destinationViewController as? GetVideo_ViewController{
destinationVC.videoToDisplay = videouserPath
}
}
}
I got in debug:
segue: ----
tableview: ----https://example.com/resources/1361929513_02-02-2016_125830.mov
Why is the segue function called before selection?

When linking UIStoryboardSegue to UITableViewCell from the storyboard, the messaging is in the order prepareForSegue, didSelectRowAtIndexPath.
Automatic Method
Bypass didSelectRowAtIndexPath altogether.
For general purpose and single selection cells, you do not need to implement didSelectRowAtIndexPath. Let the segue associated with UITAbleViewCell do the work, and handle selection like so:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailSegueIdentifier" {
if let indexPath = tableView.indexPathForSelectedRow {
print(indexPath)
}
}
}
Manual Method
If you absolutely need to do extra work before the segue, do not associate it to the UITableViewCell but to the UITableViewController. You will then need to trigger it programmatically. Inspiration here.
In IB, assign the segue to table view controller, give it an identifier (say detailSegueIdentifier), and invoke it like so.
override func tableView(tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("didSelectRowAtIndexPath")
self.performSegueWithIdentifier("detailSegueIdentifier", sender: nil)
}
Passing parameters to the segue:
Invoking performSegueWithIdentifier will also give you a chance to explicitly pass parameters, not second-guess indexPathForSelectedRow, and not rely on a global(*).
override func tableView(tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("didSelectRowAtIndexPath")
self.performSegueWithIdentifier("detailSegueIdentifier", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailSegueIdentifier" {
if let indexPath = sender as? NSIndexPath {
let name = db[indexPath.row].key
if let controller = segue.destinationViewController as? DetailViewController {
controller.name = name
}
}
}
}
(*) Don't ever rely on globals if you can help it.

Related

Prepare for segue in TableViewDelegate methods

[SOLVED]
Let me explain my problem;
I have a bunch of Event object which appears in tableView cells.
And these objects store in events array
var events: [Event]
Also i have another view controller(DetailViewContoller), in that view controller i have a variable for receiving event object from tableview.
import UIKit
class EventDetailViewController: UIViewController {
var event: Event?
override func viewDidLoad() {
super.viewDidLoad()
print(event?.name)
}
When i tapped the cells i want to pass my current event object to variable(indicated on above) in DetailViewController. So i tried this ->
extension EventListViewcontroller:UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Whenever cells tapped, related object needs to pass DetailViewController
performSegue(withIdentifier: K.Segues.eventListToDetail, sender: self)
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.Segues.eventListToDetail {
let VC = segue.destination as! EventDetailViewController
VC.event = events[indexPath.row]
}
}
}
But unfortunately after segue performed when i checked the event variable in DetailViewController it was shown as a nil. So question is, how can i pass my tapped event cell to other view controller?
Note: i've found this topic How do you test UIStoryBoard segue is triggered by didSelectRow(at:) but it didn't help my problem...
In your class
class ViewController: EventListViewcontroller {
var selectedIndex: IndexPath? = nil
}
And change your code in didSelect as
extension ViewController:UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Whenever cells tapped, related object needs to pass DetailViewController
self.selectedIndex = indexPath
performSegue(withIdentifier: K.Segues.eventListToDetail, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = self. selectedIndex, segue.identifier == K.Segues.eventListToDetail {
let VC = segue.destination as! EventDetailViewController
VC.event = events[indexPath.row]
}
}
}
Hope this helps

Swift/XCode - Why can't I pass data to the next view controller when a tableViewCell is pressed?

Apologies if this has already been answered elsewhere- I have spent the last two hours trying different things using similar suggestions but I still can't solve it!
Basically, I have a UITableView with custom cells(for different quiz topics) that when pressed, should allow the app to go to the next VC and pass an integer so the next VC knows which quiz to load. In my table view VC I have this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rowPressed = indexPath.row
print ("rowPressed = \(rowPressed) before VC changes")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is GameVC {
let vc = segue.destination as? GameVC
vc?.toPass = rowPressed
print("I ran...")
} else {
print("You suck")
}
and in my GameVC I have this:
var toPass = Int()
override func viewDidLoad() {
super.viewDidLoad()
print("toPass = \(toPass)")
The console output when run is this:
I ran...
toPass = 0
rowPressed = 3 before VC changes
So it looks like the VC changes before the previous one can send the correct value of toPass. How do I fix this?
Many thanks in advance!
According to the segue description:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped.
So, sender is a UITableViewCell and you can do like below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UITableViewCell else { return }
guard let idx = tableView.indexPath(for: cell) else { return }
guard let vc = segue.destination as? GameVC else { return }
vc.toPass = idx.row
}
The problem of your code was prepare(segue:) was invoked before tableView(didSelectRowAt:).
As suggested you have to call the performSegue method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// not necessary
//rowPressed = indexPath.row
//print ("rowPressed = \(rowPressed) before VC changes")
performSegueWithIdentifier("Your id", sender: indexPath)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Your id"{
var vc = segue.destinationViewController as! GameVC
vc.toPass = (sender as! IndexPath).row
}
}
Because prepareForSegue is called before didSelectRowAt, you can also remove the segue from the Storyboard, drag and drop holding ctrl from the UITableViewController's yellow icon in the top to the second ViewController, click on the segue, give it an identifier, so now you can call performSegue:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.pressed = indexPath.row
self.performSegue(withIdentifier: "segue identifier", sender: nil)
}

Searched item returns wrong detail view on click

I am parsing some info from JSON using Firebase. I implemented search controller into the tableview, it returns correct items while searching but the problem is that on click (didSelectRowAtIndexPath) it returns wrong info. What could cause this, can you tell me guys?
This is the arrays in which I am searching:
var brands = [String]()
And this is the filtered array:
var filteredBrands = [String]()
And this is what I am doing inside the didSelectRowAtIndexPath:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedBrand = brands[indexPath.row]
performSegueWithIdentifier("toProducts", sender: self)
}
And like this I pass the selected value:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toProducts") {
let snusProductsView = segue.destinationViewController as! SnusProductsTableViewController
snusProductsView.brandName = selectedBrand
print(self.selectedBrand)
}
}
But for some reason it passes the wrong value. It passes the first item inside tableView
Inside didSelectRowAtIndexPath you have to check is it filtered version or not if so you need to use filteredBrands instead of brands.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if isSearching {
selectedBrand = filteredBrands[indexPath.row]
} else {
selectedBrand = brands[indexPath.row]
}
I must be dumb... After posting I figured out what was the problem. My selectedBrand was wrong if the resultSearchController was active. So I fixed it like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toProducts") {
let snusProductsView = segue.destinationViewController as! SnusProductsTableViewController
snusProductsView.brandName = selectedBrand
print(self.selectedBrand)
}
}

Sending indexPath.row from didselectRowAtIndexPath to prepareForSegue via sender

I have seen many solutions however i cant get this right. I want to call prepareForSegue from the didSelectRowAtIndexPath function when a cell is tapped. Then i want to retrieve the row index that was tapped on, from inside the prepareForSegue function so i can use it to pass information to another view. I think i am calling the function correctly but there is something going wrong in my prepareForSegue function. I know its an easy solution i am just very stuck. Heres my current code
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("notificationsToAnswers", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//the below line is incorrect and should be something else
let selectedIndex = tableView.indexPathForSelectedRow?.row
//pass data on using the index
}
You should pass like this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("notificationsToAnswers", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "notificationsToAnswers" {
//the below line is incorrect and should be something else
let selectedIndexPath = sender as! NSIndexPath
let index = selectedIndexPath.row
//pass data on using the index
}
}

Swift tableView and prepareForSegue functions

I am trying to get the tableView function to change the row before it goes into the prepareForSegue function. Below are the two functions:
This function is called when a cell is clicked and it changes the row variable to the current row that is clicked
override func tableView(tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) {
row = indexPath.row
NSLog("You selected cell number: \(indexPath.row)!")
}
This function is for the segue from the cell that is clicked to the next window
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destViewController = segue.destinationViewController as! SecondView
destViewController.nameString = namesArray[getRow()]
}
I'm not sure what the getRow() method is, but it looks like you should just directly use your global row variable.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destViewController = segue.destinationViewController as! SecondView
destViewController.nameString = namesArray[row] // use your global variable row here
}

Resources