Prepare for segue not passing data - swift

I'm working on a Swift project using firebase and I have a UICollectionViewController that has all the categories, and I need to pass the category Id to the next view, but It passes an empty string
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let category = cnameArray[indexPath.row]
let catId = category.caId
func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "CatCity" {
let cityView = segue.destination as? CityViewController
cityView?.catId = catId!
} }
self.performSegue(withIdentifier: "CatCity", sender: self)
}
and in my CityViewController I have
var catId = String()
when I put a break point at the last line of the prepare func it gets hit and the console gets the value correctly while sitting a breakpoint at CityViewController gives me an empty var.
and I checked the code and my segue name and everything looks fine!

Correct setup
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let category = cnameArray[indexPath.row]
self.performSegue(withIdentifier: "CatCity", sender: category.caId)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "CatCity" {
if let cityView = segue.destination as? CityViewController {
cityView.catId = sender as! String
}
}
}

Related

Swift: having trouble getting segue to show details from selecting item in a view collection

I am trying to set a View Collection to segue to a item's details when the item's image is clicked on. Here is the code I have. However, when I try, I get the error, Thread 1: Fatal error: Index out of range, next to this line of code: destinationController.shop = shopping[indexPaths[0].row]
Here is my full code for the collection view controller. Can anyone tell me what I am doing wrong?
import UIKit
private let reuseIdentifier = "Cell"
class shopCollectionViewController: UICollectionViewController {
#IBAction func unwindToMain(segue: UIStoryboardSegue){
}
var shopping: [Shop] = [Shop(image:"blueTshirt", name:"blueTshirt", price: 10), Shop(image:"blackTshirt", name:"blackTshirt", price: 10), Shop(image:"lightblueTshirt", name:"lightblueTshirt", price: 10), Shop(image:"whiteTshirt", name:"whiteTshirt", price: 10)]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return shopping.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dataCell", for: indexPath) as! shopCollectionViewCell
// Configure the cell
let item = shopping[indexPath.row]
cell.itemImage.image = UIImage(named: item.image)
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPaths = collectionView.indexPathsForSelectedItems{
let destinationController = segue.destination as! itemDetailsViewController
destinationController.shop = shopping[indexPaths[0].row]
collectionView.deselectItem(at: indexPaths[0], animated: false)
}
}
When you use collectionView maybe you should use indexPath.item instead of indexPath.row
A more reliable way than indexPathsForSelectedItems is to pass the index path while calling performSegue.
And deselect the cell right inside didSelectItemAt
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: indexPath)
collectionView.deselectItem(at: indexPath, animated: false)
}
And replace prepare(for with
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let indexPath = sender as! IndexPath
let destinationController = segue.destination as! itemDetailsViewController
destinationController.shop = shopping[indexPath.item]
}
}

Prepare for Segue: one VC to another VC with optionals?

I have one variable, usernameSelected, that I want to send to a second VC.
First VC:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let usernameSelectedRow = picUsernameArray[indexPath.row]
usernameSelected = usernameSelectedRow
print(usernameSelectedRow) --> [this prints the right data]
print(usernameSelected) --> [this prints the right data]
performSegue(withIdentifier: "goToProfileSelected", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProfileSelected" {
let destination = segue.destination as?
OtherUserProfileViewController
print(usernameSelected) ---> [this prints nil!!!!]
destination!.usernameValue = usernameSelected ---> [this returns nil to second VC]
}
}
in did select:
self.performSegue(withIdentifier: "goToProfileSelected", sender: indexPath) //pass indexPath
now in prepare for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = sender as? IndexPath {
let name = self.picUsernameArray[index.row]
destination.usernameValue = name
}
}

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.

Segue from CollectionViewCell

I have used this segue many times in swift, but I cannot make it work from a collectionViewCell. Why????
I have control + drag from the cell to a ViewController
Here is my code
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "followSegue", sender: self.postsFollow[indexPath.item].follower)
print(self.postsFollow[indexPath.item].follower)
print("You selected cell #\(indexPath.item)!")
}
//Segue to show username in FindUserViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier! == "followSegue" {
let guest = segue.destination as! FindUserViewController
guest.pickedUser = sender as! String
let backItem = UIBarButtonItem()
backItem.title = "Back"
navigationItem.backBarButtonItem = backItem
}
}

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]
}