CollectionView Segue finding nil on receiving ViewController - ios

I have a collection view that is selecting an Item in its index and performing a segue to the next ViewController. When the next ViewController is presented, the value of my object is nil.
Here is the call in the collectionView:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let adViewVC = storyboard?.instantiateViewController(withIdentifier: adViewPageID) as? AdViewPageVC else {return}
let adChoice = adArray[indexPath.row]
adViewVC.advertisement = adChoice
performSegue(withIdentifier: adViewPageSegue, sender: self)
}
Note that the guard statement is going through and if I print a value from the adArray it has value in this function.
After I perform the segue which does open the right ViewController the advertisement object is always nil.
var advertisement : Advertisement!
override func viewDidLoad() {
super.viewDidLoad()
let title = advertisement.title
print(title)
}
This is never getting the value of the object even when I can see that it has value during the assignment on the didSelectItem function for the collection view.
What am I missing here?

When the user taps on the collectionViewCell, the app should perform segue with an indexPath as a sender:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: adViewPageSegue, sender: indexPath)
}
And prepare all of neccessary things in the prepare(for:sender:) method. And you don't have to init a viewController from the storyboard. segue.destination is enough.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == adViewPageSegue {
guard let adViewVC = segue.destination as? AdViewPageVC else {return}
let indexPath = sender as! IndexPath
let adChoice = adArray[indexPath.row]
adViewVC.advertisement = adChoice
}
}
}

You should be setting the advertisement variable in a prepare(for:sender:) function. The view controller that you are creating in the didSelect function is not being used.
Add something like this:
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let viewController = segue.destination as? PlayerViewController {
let adChoice = adArray[sender]
viewController.advertisement = sender as? STZMediaItem
}
}
And update your didSelect function to be:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: adViewPageSegue, sender: indexPath)
}

Related

How can I parse data from a UIViewController having 2 CollectionViews with swift

How do I parse data from a UIViewController having 2 different UICollectionViews? I have 2 UICollectionViews in on UIViewController.
I have tired this using didSelectItemAt and performSegue but it can't parse the data to the other screen
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showLineup" {
let gidilecekShowVC = segue.destination as! lineupViewerVC
gidilecekShowVC.showLineup = selectLineup1
} else if segue.identifier == "showLineup2" {
let gidilecekShowVC = segue.destination as! lineupViewer2VC
gidilecekShowVC.showLineup2 = selectLineup2
}
}
I know didSelectItemAt is wrong, but I don't know what the correct one is.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectLineup1 = ekranaYansitA1[indexPath.item]
performSegue(withIdentifier: "showLineup", sender: nil)
selectLineup2 = ekranaYansitA2[indexPath.item]
performSegue(withIdentifier: "showLineup2", sender: nil)
}
I'm a beginner. Thank you in advance for your help
didSelectItemAt has a collectionView parameter which gives you the collection view instance which triggered the method.
Assuming you have an IBOutlet named lineup1CollectionView, a reference to the first collection view you can perform a check
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == lineup1CollectionView {
selectLineup1 = ekranaYansitA1[indexPath.item]
performSegue(withIdentifier: "showLineup", sender: nil)
} else {
selectLineup2 = ekranaYansitA2[indexPath.item]
performSegue(withIdentifier: "showLineup2", sender: nil)
}
}

Cannot perform segue from cell with indexPath

I am trying to perform a segue and pass data to detailViewController from my cell when tapped. I have tried with dummy data and I can pass them to detailScreen, however I would like to pass data that are related to cells.When I tapped a cell I get this error "Could not cast value of type ViewController to MovieCell" with line let selectedItem = ...
Thanks for your time and effort
Here is my code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue"
{
let destination = segue.destination as! DetailViewController
let selectedItem = collectionView.indexPath(for: sender as! MovieCell)!
let character = characters[selectedItem.item]
destination.detailName = character.name
destination.detailGender = character.gender
destination.detailSpecies = character.species
destination.detailStatus = character.status
}
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "detailSegue", sender: self)
}
try use:
collectionView.indexPathsForSelectedItems.first
instead
collectionView.indexPath(for: sender as! MovieCell)!
Also I wanna recommend for you my library for such situations: Link

Passing data from CollectionView to DetailVC in Swift 4

My CollectionView should pass a class model to DetailViewController, but when I tap on a cell I get the nil error.
Fatal error: Unexpectedly found nil while implicitly unwrapping an
Optional value
The CollectionViewController is embedded programmatically on a TabBarController.
Collection View
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return soundArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SoundCell", for: indexPath) as? SoundCell {
let SoundClass = soundArray[indexPath.row]
cell.updateUI(SoundClass: SoundClass)
return cell
} else {
return UICollectionViewCell()
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "seguetosound", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "seguetosound" {
if let detailVC = segue.destination as? DetailSecondVC
let sound = sender as? SoundClass {
detailVC.SoundClass = sound
}
}
Detail View Controller
class DetailSecondVC: UIViewController, UIWebViewDelegate {
private var _SoundClass: SoundClass!
var SoundClass: SoundClass {
get {
return _SoundClass
} set {
_SoundClass = newValue
}
}
Do you know what I am missing here? I tested the segue with a simple white screen and it works but when I try to pass the data, it fails.
The correct approach is this. First, figure out how you want to trigger the segue. One option is, in didSelect, trigger the segue in code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "seguetosound", sender: self)
}
But even better, just delete didSelectItemAt completely and have the segue in the storyboard come from the cell. That way the segue is triggered automatically when the user taps the cell.
Then, in prepare, find out what index path was selected, and pull out the data from the model and pass it to the destination view controller (this might not compile, because your variable names are so atrocious that I can't read your code, but it is the correct approach generally):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DetailSecondVC" {
if let detailVC = segue.destination as? DetailSecondVC {
if let paths = collectionView?.indexPathsForSelectedItems {
let row = paths[0].row
detailVC.SoundClass = SoundClasss[row]
}
}
}
}
Edited: I thought the solution was to make the segue from the view controller instead of from the cell, but as matt said, the segue was correct from the cell but I just had to remove the implementation of didSelectItemAt

Passing data with override func prepare using collectionviewCell

I am trying to send the charName String from AvengersViewController to CharViewController.
I am using a collection view in AvengersViewController. CharName is a label in CharViewController.
What I am trying works perfectly with a table view but I can't get it to work using collectionViews...
I am using "lastItemSelectedA" to indicate the index of the label from my avengers array. The passing of data works... I can't get the index of the collectionViewItem to pass with the first segue, thus, making it null. By using the default value of 0 I have been able to notice that it does work, however, it does not change the value of lastItemSelectedA when the cell is pressed but after... Or at least it does not update the variable.
I have already tried at least 5 implementations from solutions on stack.
extension AvengersViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
lastItemSelectedA = indexPath.item
//self.performSegue(withIdentifier: "openToCharA", sender: indexPath.item)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let identifier = segue.identifier else { return }
switch identifier {
case "openToCharA":
if let destination = segue.destination as? CharViewController {
destination.charName = avengers[lastItemSelectedA ?? 0].name
}
//destination.sounds = sounds
//guard let indexPath = collectionView.indexPathsForSelectedItems else {return}
//let sounds = fallen[lastItemSelectedF!].sounds
default:
print("unexpected segue identifier")
}
}
If prepare(for segue is called then you've connected the segue from the collection view cell (rather than from the controller).
In this case delete
var lastItemSelectedA : Int
and
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
lastItemSelectedA = indexPath.item
//self.performSegue(withIdentifier: "openToCharA", sender: indexPath.item)
}
and get the index path of the collection view cell from the sender parameter
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openToCharA" {
let cell = sender as! UICollectionViewCell
let indexPath = collectionView.indexPath(for: cell)!
let destination = segue.destination as! CharViewController
destination.charName = avengers[indexPath.item].name
}
}
Force unwrapping the optionals is fine in this case. The code must not crash if everything is hooked up correctly and if it does it reveals a design mistake.

How to send a selected collectionview cell to another view controller (ios)?

I'm being able to detect the selected row (image) in my collection view, But I need to send it to another view controller. Here is a part of the code :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionViewCell {
cell.cellImage.image = UIImage(named: images[indexPath.row])
return cell
} else {
return CollectionViewCell()
}
}
//Printinig the selected image ID in console
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
SelectedItem = indexPath.row + 1
print(SelectedItem)
}
//Navigate to MPViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DestViewController = segue.destination as! MPViewController
DestViewController.labelText = String(SelectedItem)
}
}
Initialize a variable first
var imageToPass: UIImage!
Then update didSelectItemAt func
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
SelectedItem = indexPath.row + 1
print(SelectedItem)
self.imageToPass = UIImage(named: images[SelectedItem])
performSegue(withIdentifier: "TargetVC", sender: imageToPass) //here you give the identifier of target ViewController
}
Go to your TargetVC and initialize a variable
var getImage: UIImage!
Then override the function in previous VC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TargetVC" {
if let targetVC = segue.destination as? TargetVC {
if let imageToPass = sender as? UIImage {
TargetVC.getImage = imageToPass
}
}
}
}
//Printinig the selected image ID in console
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
self.SelectedItem = indexPath.row + 1
self.selectedImage = UIImage(named: images[indexPath.row]);
print(SelectedItem)
}
//Navigate to MPViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DestViewController = segue.destination as! MPViewController
DestViewController.imageSelected = self.selectedImage;
DestViewController.selectedItem = String(self.SelectedItem);
}
Now in MPViewController you can use the data self.imageSelected and self.selectedItem as per your requirements.
Take one instance variable in your destination class and set value of it in prepare for segue and then in viewDidload set that string to your label's text like,
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DestViewController = segue.destination as! MPViewController
DestViewController.yourText = String(SelectedItem)
}
ans in viewDidload
yourLabel.text = yourText

Resources