How to remove custom view from invisible cell in collection view - ios

I am trying to change color of selected custom view in collection view. I am using didSelectItem and didDeselectItem method of collection view for the same.
I am facing error when i am going to change invisible cell in collectionview.
Visible cell of collection view is perfectly working
My code is below
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = colCellSize.cellForItem(at: indexPath) as! colSizeDetails
cell.viewDetails.backgroundColor = UIColor.gray
cell.lblSize.textColor = UIColor.white
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = colCellSize.cellForItem(at: indexPath) as! colSizeDetails
cell.viewDetails.layer.borderWidth = 2.0
cell.viewDetails.layer.borderColor = UIColor.gray.cgColor
cell.lblSize.textColor = UIColor.gray
cell.viewDetails.backgroundColor = UIColor.white
}

This line is wrong:
let cell = colCellSize.cellForItem(at: indexPath) as! colSizeDetails
Here, you are casting the cell as a colSizeDetails. The colSizeDetails should be it's own class with a subclass of UICollectionViewCell. Your class names should never start with a lowercase letter. You need to change your class name to ColSizeDetails and then re-write that line with the capital letter. This may be the issue as I cannot see you force unwrapping anymore optionals

Related

How to reference/update label inside a reusable cell of a collection view?

I have a collection view with a reusable cell. That cell has a background, label and button. I can reference which background is in each cell. I would like to update the text in the label when the button is pressed based on which background that cell has. I am having trouble referencing the cell. let cell = collectionView.cellForItem(at: indexPath) gives me an error. How do I reference this cell?
I am ok if the label gets reset when the user scrolls the collection view.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyColectionCell
cell.CellBG.image = UIImage(named: ButtonBGs[indexPath.row])
cell.CellBG.layer.cornerRadius=10
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowOffset = CGSize(width: 1, height: 10)
cell.layer.shadowOpacity = 0.3
cell.layer.shadowRadius = 10
cell.layer.masksToBounds = false
cell.Info.tag = indexPath.row
cell.Info.addTarget(self, action: #selector(Info), for: .touchUpInside)
cell.CellText.text = " "
cell.CellText.tag = indexPath.row
return cell
}
#objc func Info(sender: UIButton){
let indexPath = IndexPath(row: sender.tag, section: 0)
let cell = collectionView.cellForItem(at: indexPath) //gives me error "Reference to member 'cellForItem' cannot be resolved without a contextual type"
if((ButtonBGs[indexPath.row])=="bt-tower"){
cell.CellText.text = "New Text"
}
}
I would recommend a different approach to this. The code you provided indicates all of this could be performed in the cell itself.
Give it a property of type String that holds the imagename, assign it inside of func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: and perform the button action/comparison inside the Button #IBAction.
Also, that code manipulating the layer should also be in the cell itself. The cellForItemAt indexpath: function should only provide the data the cell presents.
I am using an array for the label text and keeping the labels alpha at 0. When I need to display it I change the alpha and reload the data self.myCollection.reloadItems(at: [indexPath]) This way only the text for the indexed cell is shown.

UICollectionView - random cells are selected

I have a Horizontal UICollectionView like the horizontal Calender in iOS.
Paging is enabled but not allowsMultipleSelection.
self.allowsMultipleSelection = false
self.isPagingEnabled = true
There are only 5 cells per page.
let cellSize = CGSize(width: self.view.frame.width / 5 , height: 60)
CollectionView's height is also 60.
didSelectItemAt change background color to .red and didDeselectItem resets it to .white.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
if let cell = cell {
cell.backgroundColor = .red
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
if let cell = cell {
cell.backgroundColor = .white
}
}
The collection view has multiple sections and rows. If I select a cell in the first visible page and scroll, random cells are selected in the next visible pages. That is to say random cells are red in the next pages. I do not want this to be so. I want to select/change color of cells manually.
How can I fix this?
Don't forget that UICollectionView has embedded reusing mechanism, so you should deselect your cells in the method "prepareToReuse" directly inside the cell class.
Take a class-level variable, say index
var index = -1
As you have said that multiple selections are not allowed so the following will do the job for you
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
index = indexPath.item
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
if let cell = cell {
cell.backgroundColor = indexPath.item == index ? .red : .white
}
}
Whenever user tap on any cell we save the position in index variable and then call the reloadData() to notify collectionView about the change
In cellForRowAt we check if the current cell us selected we set the color to red otherwise white
First, if you want to preserve multiple selection, you have to remember your selected ones in an array since it would get lost if a cell gets recycled and reused. For that use something like a [IndexPath] type). If one selected cell is enough, you could use a non-array version of below code.
var selectedItems: [IndexPath] = []
Then, do your recoloring in your cell's cellForItemAt(:):
cell.backgroundColor = selectedItems.contains(indexPath) ? .red : .white
Your didSelectItemAt delegate function should look like:
if !selectedItems.contains(indexPath) { selectedItems.append(indexPath)}
collectionView.cellForItem(at: indexPath)?.backgroundColor = .red
and your didDeselectItemAt delegate function:
if let index = selectedItems.firstIndex(of: indexPath) { selectedItems.remove(at: index) }
collectionView.cellForItem(at: indexPath)?.backgroundColor = .white
This should actually work. Let me know if we have to do adjustments.

UIcollectionView weird cell recycling behaviour

I have a UICollectionView with flow layout, about 140 cells each with a simple UITextView. When a cell is recycled, I pop the textView onto a cache and reuse it later on a new cell. All works well until I reach the bottom and scroll back up. At that point I can see that the CollectionView vends cell number 85, but then before cell 85 is displayed it recycles it again for cell 87 so I now lose the content of the cell I had just prepared.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath) as! FormCollectionViewCell
let textView = Cache.vendTextView()
textView.text = "\(indexPath.row)"
cell.addSubview(textView)
cell.textView = textView
return cell
}
And on the UIcollectionViewCelC
override func prepareForReuse() {
super.prepareForRuse()
self.textView.removeFromSuperView()
Cache.returnView(self.textView)
}
I would have thought that after cellForItemAtIndexPath() was called, it would then be removed from the reusable pool of cells but it seems it is immediately being recycled again for a neighbouring cell. maybe a bug or I am possibly misunderstanding the normal behaviour of UICollectionView?
As I understand it, what you're trying to do is just keep track of cell content - save it when cell disappears and restore it when it comes back again. What you're doing can't work well for couple of reasons:
vendTextView and returnView don't take indexPath as parameter - your cache is storing something and fetching something, but you have no way of knowing you're storing/fetching it for a correct cell
There's no point in caching the whole text view - why not just cache the text?
Try something like that:
Have your FormCollectionViewCell just have the text view as subview, and modify your code like so:
class YourViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
{
var texts = [IndexPath : String]()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath)
if let formCell = cell as? FormCollectionViewCell {
cell.textView.text = texts[indexPath]
return cell
}
}
func collectionView(_ collectionView: UICollectionView,
didEndDisplaying cell: UICollectionViewCell,
forItemAt indexPath: IndexPath)
{
if let formCell = cell as? FormCollectionViewCell {
{
texts[indexPath] = formCell.textView.text
}
}
}

Changing the text of a label when a collection view cell is tapped in swift 4.0

I am having a problem with changing the text of a label when a collection view cell is tapped. I have tried using didSelectItemAt and didHighlightItemAt but nothing worked. Here's what my cell looks like:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.subjectName.text = "Selected"
}
You need
let cell = collectionview.cellForItem(at: indexPath) as! CollectionViewCell
cell.subjectName.text = "Selected"
but note because of cell dequeuing this change is temporary when the cell is still shown if you scroll around you may find another text inside that index , so reflect the changes in the array model of the collection and reload that indexPath
var statesArr = ["Selected","Default",,,,,,,,,,]
inside didSelectItemAt
statesArr[indexPath.row] = "Selected"
self.collectionView.reloadItems(at:[indexPath])
inside cellForItemAt
let cell = ///
cell.subjectName.text = statesArr[indexPath.row]

Changing text color in UICollectionView in Swift

I have a UICollectionView with cells that contain UILabels that update dynamically. When I select a cell, I have the background color of the cell change, but I want the text color in the label to change as well. Currently, I'm using the following code:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
cell = collectionView.dequeueReusableCellWithReuseIdentifier("targetCell", forIndexPath: indexPath) as UICollectionViewCell
var label : UILabel = cell.viewWithTag(100) as UILabel
label.textColor = UIColor.whiteColor()
}
However, on selecting the cell, the text color does not update with the new color. Any ideas why?
Because you are dequeuing a reusable cell and update it.
Basically, dequeueReusableCellWithReuseIdentifier:forIndexPath: is only used in collectionView:cellForItemAtIndexPath:.
Use cellForItemAtIndexPath to get the selected cell.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
let label = cell.viewWithTag(100) as? UILabel
label?.textColor = UIColor.white
}
}

Resources