I am trying to do a Collection View
whereby someone selects a cell
and for each selection it takes them to another View Controller
that holds content of the selection. However I'm running into difficulties as once I apply this line of code to didSelectItemAtIndexPath
;
self.performSegueWithIdentifer("showDetail", sender: self)
and then run it in the Simulator the cell
selection is working according the indexPath
but its remembering the selections each time I select new cell
. So for example each cell
has a photo and label and if I select the first cell
in the indexPath
the segue
takes me first to blank view and then to my selected cell
. If I select another cell
, number 3 on the indexPath
the blank view is now the first cell
from my previous choice after which it takes to my selected third cell
. Its doing that every time. If I remove the performSegueWithIdentifer
code (from Xcode 6.2 (in 6.1.1 it was random)) the selection is my previous choice and never my 'selectedCell', but then at least its only selecting once instead of twice to get to a view
. There is something going wrong on the indexPath
. This is the code for my prepareForSegue
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "showDetail" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
detailVC.selectedImageName = selectedImage
detailVC.selectedLabel = selectedLabels
}
}
I'm stuck on what to do & what solution to apply. Do I keep performSegueWithIdentifer
code & create an Equatable
to implement find(array, selection)
on the indexPath
? Or could I write a loop, (which seems much easier), that would run through the indexPath
based upon the selections and that would remove the cell that is no longer selected. However I'm not sure what condition to write in the loop because I don't know the value of the property of the 'selectedCell' because its optional.
for (index, value) in enumerate(cellItems) {
//something here to remove 'selectedItem' in the indexPath
}
If I remove performSegueWithIdentifer
code from didSelectItemAtIndexPath
what can I do in my prepareForSegue
to get the selection on the correct indexPath?
EDIT the complete code at didSelectItemAtIndexPath
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedImage = cellImages[indexPath.row] as String
selectedLabels = cellLabels[indexPath.row] as String
self.performSegueWithIdentifer("showDetail", sender: self)
}
I've tried changing sender in the performSegueWithIdentifer
to indexPath
but the problem still remains.
EDIT 2 Complete code to my CollectionViewController
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet weak var collectionView: UICollectionView!
var selectedImage = String()
var selectedLabels = String()
var cellImages:[String] = ["1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg", "6.jpg", "7.jpg", "8.jpg", "9.jpg", "10.jpg", "11.jpg", "13.jpg", "14jpg"]
var cellLabels:[String] = ["Photo 1", "Photo 2", "Photo 3", "Photo 4", "Photo 5", "Photo 6", "Photo 7", "Photo 8", "Photo 9", "Photo 10", "Photo 11", "Photo 12", "Photo 13", "Photo 14"]
func collectionView(collectionView: UICollectionView, numberOfNumberItemsInSection: Int) -> Int {
return cellImages.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: PhotoViewCell = collectionView.dequeueReuseableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as PhotoViewCell
cell.labelCell.text = cellLabels[indexPath.row]
cell.ImageCell.image = UIImage(named: cellImages[indexPath.row])
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedImage = cellImages[indexPath.row] as String
selectedLabels = cellLabels[indexPath.row] as String
self.performSegueWithIdentifer("showDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "showDetail" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
detailVC.selectedImageName = selectedImage
detailVC.selectedLabel = selectedLabels
}
}
}
PhotoViewCell
class PhotoViewCell: UICollectionViewCell {
@IBOutlet var labelCell: UILabel!
@IBOutlet var ImageCell: UIImage!
}
EDIT 3 - Amended
I tried your suggestion and unfortunately the problem is still persisting on double views - it's still passing two views before it takes me to the actual selected cell
. I also amended the code slightly in the didSelectItemAtIndexPath
but it still didn't fix the problem.
if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? PhotoViewCell {
performSegueWithIdentifier("showDetail", sender: cell)
}
However following your other suggestion, in my StoryBoard
I have added a segue
from my Collection View
cell
to my DetailViewController
, which has the identifier "showDetail". If I remove segue
nothing can be selected from my cells
.
Although it seems the performSegueWithIdentifer
code is the trigger for the double views because when I remove it, the cell
is only being selected once, the problem was that the indexPath
of the cell
selection was not correct, because it's first selecting on a blank view (is that to do with the showDetail segue
?), which then puts my indexPath
out of sync.
EDIT - Solved
This stopped the double selections (the performSegueWithIdentifier
line was removed): -
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
cellLabels[indexPath.row] as String
cellImages[indexPath.row] as String
}
}
Many Thanks for your help !!!!
(NOTE: I updated this for Swift 4 and more modern practices.)
I stick to UIView
objects as much as possible.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
performSegue(withIdentifier: "showDetail", sender: cell)
}
Then in prepare(for:sender:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case "showDetail":
guard let indexPath = (sender as? UIView)?.findCollectionViewIndexPath() else { return }
guard let detailViewController = segue.destination as? DetailViewController else { return }
detailViewController.selectedImageName = cellImages[indexPath.row]
detailViewController.selectedLabel = cellLabels[indexPath.row]
default: return
}
}
I used an extension I created a while ago findCollectionViewIndexPath()
extension UIView {
func findCollectionView() -> UICollectionView? {
if let collectionView = self as? UICollectionView {
return collectionView
} else {
return superview?.findCollectionView()
}
}
func findCollectionViewCell() -> UICollectionViewCell? {
if let cell = self as? UICollectionViewCell {
return cell
} else {
return superview?.findCollectionViewCell()
}
}
func findCollectionViewIndexPath() -> IndexPath? {
guard let cell = findCollectionViewCell(), let collectionView = cell.findCollectionView() else { return nil }
return collectionView.indexPath(for: cell)
}
}
I have a suspicion that you have a segue in the storyboard already and don't need func collectionView(, didSelectItemAtIndexPath:)
, but either way, the prepare segue should work.
Swift 3.0
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let iPath = self.collectionView.indexPathsForSelectedItems
let indexPath : NSIndexPath = iPath![0] as NSIndexPath
let rowIndex = indexPath.row
print("row index = \(rowIndex)")
}
来源:https://stackoverflow.com/questions/29183124/swift-uicollectionview-selecting-cell-indexpath-issues