How to send indexpath when performing segue on collectionViewCell? - swift

I have a collection view controller with similar cells and I want each cell open next viewController with specific content. I think I must send indexpath.row with the segue but in this part I face an error:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let newViewController = segue.destination as? myCollectionViewController {
newViewController.passedValue = indexPath.row // exactly here
}
}

There are a couple of things wrong in your code. indexPath.row variable is not declared in your prepare function. To pass the selected cell info to another view controller you first need to add a global variable in your class.
var selectedCell = 0
Next implement the function didSelectRowAtIndexPath in your class. This function is called every time you select a cell. In this function get the cell info and assign it to selectedCell var
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
selectedCell = indexPath.row
}
Now in your prepare method use this var value and pass it on to the next controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourIdentifier" {
let VC = segue.destination as! YourViewController
VC.passed = selectedCell
}
}
Hope this helps.

In the above code:
newViewController.passedValue = indexPath.row // exactly here
what is the value of indexPath here? Is is an instance variable?
Instead of using segue, use UICollectionViewDelegate method:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
//Push your controller here
}

Related

Second Controller not receiving data

I have two controllers and I am trying to pass data from the first one to second, but it is not receiving at all. I tried passing data as Properties and also using prepareForSegue, but some how, second view controller is not receiving the value from the first controller. First Controller is called MainViewController and second one is FactViewController. I am using this extension bellow because I am using a contentView inside my first screen. When I tap a cell from the content view I want to perform the data transference. factValue constant is receiving normally the data I want, but when I transfer it to the second controller (FactViewController) it doesn't transfer.
USING PROPERTY WAY:
extension MainViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let factValue = facts!.result[indexPath.row].value
let fvc = FactViewController()
fvc.receivedValue = factValue
performSegue(withIdentifier: "segueMainscAndFactsc", sender: nil)
}
USING PREPAREFORSEGUE WAY:
extension MainViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let factValue = facts!.result[indexPath.row].value
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let destinationVC = segue.destination as! FactViewController
destinationVC.receivedValue = factValue
}
}
}
This is my second viewController (FactViewController):
class FactViewController: UIViewController {
var receivedValue: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print(receivedValue)
}
}
When I print the variable receivedValue in the second Controller inside viewDidLoad it still prints an empty String. Does any one has a suggestion to fix it?
Well, the first way is very obviously invalid. You say let fvc = FactViewController() and set its receivedValue, but then you throw it away and instead you trigger a segue which involves a completely different FactViewController. So the one you set the receivedValue is not the one whose view got loaded.
The second way is also invalid because your prepareForSegue is buried inside another a function (and has the wrong signature), so it never even gets called.
What you actually want is more like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "segueMainscAndFactsc", sender: nil)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! FactViewController
let factValue = // ???
destinationVC.receivedValue = factValue
}
Note that I've corrected your prepare signature.
The hard part here is working out what factValue should be. But for now, just make it some arbitrary value to convince yourself that this works! Then you can work on obtaining the correct value to hand over to the destination vc.

Use perform segue into a collection view

i need to pass my data into collection view with index path.
i'm trying to use this code but i don't know why i have 2 times the result, one time it's ok and the second one is nil.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showDetails", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let cell = sender as? UICollectionViewCell,
let indexPath = self.collectionView.indexPath(for: cell) {
let secondViewController = segue.destination as! CollectionDetailViewController //Cast with your DestinationController
//Now simply set the title property of vc
secondViewController.people = person[indexPath.row]
}
}
You are getting 2 calls to prepare(for:sender:) because your segue is wired from your UICollectionViewCell. This will call prepare(for:sender:) for you, so you don't need this code:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showDetails", sender: indexPath)
}
Delete it, and you should be good to go.

How to Send a segue in didSelectItemAt

I am trying to make a segue that sends a string to a variable in the next view controller. This string is the text inside a label that is inside a collection view cell, the text inside the label is from CoreData. When the user presses the cell, it is meant to segue to the next view controller and have the text of the cell previously selected in a variable. I am not sure how to do this, this is my didSelect function which is able to get the text of the selected cell:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellP", for: indexPath) as! CCCollectionViewCell
let project = projectList[indexPath.row]
if project.name! == "Hi " {
print("Yes")
}
else {
print("no")
}
print(project.name!)
}
The problem is I am not sure how to send this value (project.name!) of the selected cell in a segue to the next view controller variable.
Try like this way.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! NextViewController //Your ViewController class
if let cell = sender as? UICollectionViewCell,
let indexPath = self.collectionView.indexPath(for: cell){
let project = projectList[indexPath.row]
print(project.name)
vc.name = project.name
}
}
Now simply in your NextViewController create one property of type String.
var name = String()

UICollectionView cell is nil

I'm working on a collection view that gets populated with images that I have on Firebase. Everything works fine, but when I try to perform segue I get "unexpectedly found nil while unwrapping an Optional value" for this line:
if let indexPath = self.collectionView?.indexPath(for: sender as! UICollectionViewCell){}
I have seen many working examples here in SO and apparently they all work fine with that line.
Here is the rest of the relevant code:
//grab Firebase objects in viewdidload and put them into productsArray
var productsArray = [ProductsModel]()
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return productsArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imageView = cell.viewWithTag(1) as! UIImageView
//imageView.image = imageArray[indexPath.row]
let getUrl = productsArray[indexPath.row].productImg
imageView.loadUsingCache(getUrl!)
imageView.layer.borderColor = UIColor.lightGray.cgColor
imageView.layer.borderWidth = 1
imageView.layer.cornerRadius = 0
return cell
}
//NAVIGATION
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "itemSegue", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "itemSegue"){
let destinationController = segue.destination as! ItemViewController
if let indexPath = self.collectionView?.indexPath(for: sender as! UICollectionViewCell){
destinationController.getProduct = productsArray[indexPath.row]
}
}
}
Also, I double checked everything is connected and set in the storyboard.
Thanks in advance!
In the following function, you send the sender parameter as nil:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "itemSegue", sender: nil)
}
Then in the following function, you receive the sender parameter and try to cast it (sender as! UICollectionViewCell). This parameter will always be nil.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "itemSegue"){
let destinationController = segue.destination as! ItemViewController
if let indexPath = self.collectionView?.indexPath(for: sender as! UICollectionViewCell){
destinationController.getProduct = productsArray[indexPath.row]
}
}
}
If you don't want it to be nil, then don't invoke the performSegue(withIdentifier:sender:) function with a nil sender. Send a valid object. In this case, it seems you want sender to be of type UICollectionViewCell. So send the cell in the performSegue function.
Edit: Nirav D makes a good point in mentioning that there is no reason to send the cell since it just gets converted back into indexPath anyway. We could solve this entire issue by doing:
performSegue(withIdentifier: "itemSegue":, sender: indexPath)
and:
if let indexPath = sender as? IndexPath {
destinationController.getProduct = productsArray[indexPath.row]
}

Segue not getting selected row number

I am passing data from a table view controller to a detail view. I tried using indexPath.row directly in my prepareForSegue method, however it displays an error of
use of unresolved identifier 'indexPath'
So, after searching the web, I set up the variable indexOfSelectedPerson which is assigned the value of indexPath.row. The problem when I run the app in the simulator is that prepareForSegue is getting the initial value of indexOfSelectedPerson (0), then getting the value of the selected row only after I click it. So, when I hit the back button in the Simulator and select a different row, the detail view shows the info of the row I selected the previous time.
import UIKit
class MasterTableViewController: UITableViewController {
var people = []
var indexOfSelectedPerson = 0
override func viewDidLoad() {
super.viewDidLoad()
people = ["Bob", "Doug", "Jill"]
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return people.count
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView!.dequeueReusableCellWithIdentifier("personCell", forIndexPath: indexPath) as UITableViewCell
cell.text = "\(people[indexPath.row])"
return cell
}
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
indexOfSelectedPerson = indexPath.row
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if let mySegue = segue.identifier {
if mySegue == "personDetails" {
let detailsVC: DetailTableViewController = segue.destinationViewController as DetailTableViewController
detailsVC.selectedPersonName = "\(people[indexOfSelectedPerson])"
}
}
}
}
So, selecting Doug when the app first starts in the simulator displays the details for Bob because indexPathOfSelectedPerson is 0. Hitting the back button and then selecting Jill displays the details for Doug because indexPathOfSelectedPerson became 1 when I clicked on Doug the previous time. I'm guessing the problem stems from the order in which the methods are called.
The best way to do this kind of thing is not to use the delegate.
Updated Swift 4+
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedIndex = tableView.indexPath(for: sender as! UITableViewCell)
// Do your stuff with selectedIndex.row as the index
}
Original Answer
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
let selectedIndex = self.tableView.indexPathForCell(sender as UITableViewCell)
// Do your stuff with selectedIndex.row as the index
}
Swift 3 update:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedIndex = tableView.indexPath(for: sender as! UITableViewCell)!
// Do your actual preparing here...
}
//In didSelectRowAtIndexPath, you use this code:
func tableView(tvMain: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("yourSegueName", sender: nil)
}
//And prepareForSegue to get IndexPath and send your value
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject!){
let indexPath : NSIndexPath = self.yourTableView.indexPathForSelectedRow()!
//make sure that the segue is going to secondViewController
let detailsVC = segue.destinationViewController as DetailTableViewController
detailsVC.selectedPersonName = "\(people[indexPath.row])"
}