Passing data with override func prepare using collectionviewCell - ios

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.

Related

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

CollectionView Segue finding nil on receiving ViewController

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

UICollectionView Controller and Passing CloudKit data in Prepare for Segue

I have successfully populated a UICollectionView controller with data and images from a CloudKit record, however I am having a problem passing the selected cell to the details UIViewController. Here is my code thus far -
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.staffArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> StaffCVCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! StaffCVCell
let staff: CKRecord = staffArray[indexPath.row]
let iconImage = staff.object(forKey: "staffIconImage") as? CKAsset
let iconData : NSData? = NSData(contentsOf:(iconImage?.fileURL)!)
let leaderNameCell = staff.value(forKey: "staffName") as? String
cell.leaderNameLabel?.text = leaderNameCell
cell.leaderImageView?.image = UIImage(data:iconData! as Data);
return cell
}
func prepare(for segue: UIStoryboardSegue, sender: StaffCVCell) {
if segue.identifier == "showStaffDetail" {
let destinationController = segue.destination as! StaffDetailsVC
if let indexPath = collectionView?.indexPath {
let staffLeader: CKRecord = staffArray[indexPath.row]
let staffID = staffLeader.recordID.recordName
destinationController.staffID = staffID
}
}
}
The problem occurs at the line -
let staffLeader: CKRecord = staffArray[indexPath.row]
where I am presented with the error -
Value of type '(UICollectionViewCell) -> IndexPath?' has no member
'row'
I have tried replacing row with cell, however this only presents another error -
Value of type '(UICollectionViewCell) -> IndexPath?' has no member
'cell'
I am sure there is something fundamental i'm missing but cannot see it. Any pointers greatly appreciated.
If your segue is triggered by touching a cell, you want something along the lines of code below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showStaffDetail" {
let destinationController = segue.destination as! StaffDetailsVC
// Find the correct indexPath for the cell that triggered the segue
// And check that the sender is, in fact, a StaffCVCell
if let indexPath = collectionView?.indexPath(for: sender), let sender = sender as? StaffCVCell {
// Get your CKRecord information
let staffLeader: CKRecord = staffArray[indexPath.item]
let staffID = staffLeader.recordID.recordName
// Set any properties needed on your destination view controller
destinationController.staffID = staffID
}
}
}
Note that I've changed the method signature back to the standard method signature.

How to Send Collection View Cell Text via Segue

I am trying to send a label that is contained within a collection view cell to another view controller with a segue.
My plan is that when a user taps on the collection view cell, the app then segues to the next view controller where the navigation bar's title displays the text of the label in the collection view cell selected.
I have tried this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CCCollectionViewCell
//itemSelected = items[indexPath.row] as String
itemSelected = cell.pLabel.text!
print(itemSelected)
}
and in prepareForSegue I have not written any code as I am not sure how this works.
I commented out the block '..items[indexPath.row] as String' because it won't show the label and added the print function to see what will output but it only outputs the name given in the storyboard.
I am very new to Xcode so am not familiar with didSelect and prepareForSegue. All I am trying to do is to send the text within a collection view cell to another view controller with a segue.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "contentVideoSegue", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contentVideoSegue"{
let selectedIndexPath = sender as? NSIndexPath
let videoContentVC = segue.destination as! VideoContentController
videoContentVC.text = items[selectedIndexPath.row] as String
}
}
in the hope of helping :)
From your code you are not calling the performSegue(withIdentifier:sender:) so you probably have created segue from the CollectionViewCell to DestinationViewController. So get the indexPath using this cell in prepareForSegue method.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let cell = sender as? UICollectionViewCell,
let indexPath = self.collectionView.indexPath(for: cell) {
let vc = segue.destination as! SecondViewController //Cast with your DestinationController
//Now simply set the title property of vc
vc.title = items[indexPath.row] as String
}
}
So you don't need to set up the cell in didSelect because you're already doing that in cellForItemAtIndexPath.
Rather you'll want to call performSegue(withIdentifier: "SegueName", sender: indexPath) in your didSelectItemAtIndexPath. Then in your prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let indexPath = sender as? IndexPath else { return }
let collectionCell = collectionView.cellForItem(at: indexPath)
let textToPass = collectionCell.textLabel.text
let detailVC = segue.destination as? DetailViewController
detailVC.passedInString = textToPass
}

Resources