I have a button inside a UICollectionViewCell (custom class) in a UICollectionView. When the button is clicked, it changes its background color. Works perfectly at first, but when I change the background color of the main view, I can't update the background color of the button. The background colors are stored in an array and here's the cell set-up function:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("Rebuilding cells")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ColorCollectionViewCell
cell.button.tappedColor = backgroundColors[section]
cell.button.originalColor = backgroundColors[section + 1]
cell.button.delegate = self
cell.button.addTarget(self, action: #selector(didHitButton), for: .touchUpInside)
return cell
}
So if I start with section as 1, it loads the cell's button with the proper background color (black which is the first color in the section array. Then, I set the count to zero and grid.reloadData() which successfully wipes out the cells.
But then when I set section to 2 and reset the total cell count again, the cells don't adopt the new background color (section 2 color is green). They stay black from section 1.
How would I go about telling the UICollectionView to repaint the button background color in the cell when the data updates, where that background color comes from an array based on a variable for the index?
I think you should be using the .backgroundColor property of the views to set the background color to display it.
Other than that, with each tableView.reload() all your tableView delegate methods are called again and your cells are re-binded with the data that you provide... so you have to treat them like they're un-initialized. This could also point out to, that if you're not getting the right colors assigned, black might be their default color and maybe its not working for section 1 as well or you should check the data stored in your array (you can debug or print them).
For a cleaner flow of logic just do:
if indexPath.section == 0 {
// do this for section 0
} else if indexPath.section == 1 {
// do this for section 1
} else {
// for all other if any
}
Hope this helps you in someway...
Related
I'm pretty new to Swift and programming in general, so sorry for the simple question:
I want to develop a calendar that uses an (horizontally scrollable) UICollectionView as interface. Every cell of the UICollectionView is supposed to have a label with the number of the respective day and the weekday.
For this purpose I have an dateArray that stores the date-objects. The setupCell- method is putting the respective data onto the labels of the UICollectionViewCell.
Cells that show Sundays should be highlighted by having a different background-color than the other cells.
I tried to implement this functionality in the cellForItemAt - method, but got stuck there.
My function looks like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier, for: indexPath) as! MyCollectionViewCell
let dayFormatter = DateFormatter()
let weekdayFormatter = DateFormatter()
dayFormatter.dateFormat = "dd"
weekdayFormatter.dateFormat = "EEE"
cell.setupCell(day: dayFormatter.string(from: dateArray[indexPath.item]), weekday: weekdayFormatter.string(from: dateArray[indexPath.item]))
if Calendar.current.component(.weekday, from: dateArray[indexPath.item]) == 1 {
cell.backgroundColor = UIColor.gray
}
return cell
}
With this function the Sundays are highlighted as planned, but only as long as I don't scroll. After some scrolling at the end all cells will be highlighted.
I'm thankful for every hint to solve the issue.
The UICollectionViewCells are being reused. Hence the name dequeueReusableCell. This means that, for example, the cell at index 0 is the same cell as at index 30. When you have set the color of the cell at index 0 to UIColor.gray, the cell at 30 will also be gray, unless you set it to another color. Because all cells are going to be reused, and all cells will eventually be a "Sunday" at some point, they will all become colored.
There is an easy fix for this, do not only set a color for the ones you want to have a color but also do the opposite.
For example:
if Calendar.current.component(.weekday, from: dateArray[indexPath.item]) == 1 {
cell.backgroundColor = UIColor.gray
} else {
cell.backgroundColor = UIColor.white
}
I haven't tried it myself before, but there seems to be another way to achieve this as well. You could also implement the prepareForReuse() (docs) method in the UICollectionViewCell itself. You could do this by adding the following to the cell:
override func prepareForReuse() {
super.prepareForReuse()
backgroundColor = UIColor.white // Or set to another color you want
}
Another method would be to set the backgroundColor in the setupCell() you created for your cell. This will be called every time a cell is reused, so this might be an excellent place to do this as well. Just apply the same logic as above (if Sunday -> gray, else -> white).
I've created a custom collectionView cell class that contains a UIButton. So each cell contains it's own button that a user can press. However whenever one cell button is tapped it causes background color changes to buttons in other cells. I want it to where only the selected button's background color is changed instead and to prevent the change to other buttons. Below is my code:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! SubjectsCell
cell.subjectButton.setTitle(subjectNames[indexPath.row], for: .normal)
cell.subjectButton.addTarget(self, action: #selector(updateBackgroundColor(_:)), for: .touchUpInside)
if cell.subjectButton.isChosen == selectedState{
cell.subjectButton.backgroundColor = .green }
else if cell.subjectButton.isChosen != selectedState {
cell.subjectButton.backgroundColor = UIColor(red: 34.0/255.0, green: 210.0/255.0, blue: 255.0/255.0, alpha: 1)
}
return cell
I realize that dequeueReusableCell causes some cells to be reused, if this is my problem how I prevent cell changes happening to random cell's and only affect the cell that the tapped button is in.
You're right that dequeueReusableCell(withReuseIdentifier:for:) is the source of the issue here! You just need to have your cell class override UICollectionReusableView's prepareForReuse() method and reset the background to whatever it needs to be.
You may need to change your data model so you have a way of remembering which cells have had the button pressed since the cells can't hold on to that data without causing problems.
Also note that calling addTarget inside that collectionView(_:cellForItemAt:) may cause problems later, targets are typically only added once when the object is set up initially. To avoid issues you should set that up either in the nib / storyboard as an action, or when you setup the view objects if you're doing programmatic view setup.
Here's Apple's documentation on prepareForReuse().
You can do something like this:
class SubjectsCell : UICollectionViewCell
{
#IBAction func buttonAction(_ sender: UIButton) {
updateBackgroundColor()
}
}
so the cell view background update should present in SubjectsCell because its a view update not in job of viewController.
Currently I am using table.sectionIndexBackgroundColor = .clear which makes the section index semi-transparent, causing what you can see in screenshot below.
I am trying to remove the semi-transparent overlay, which ends above the home button area on iPhone X. Another option is to extend the section index background throughout this area and set a non-transparent colour, because this design on rounded display looks really weird.
Any ideas how to do this?
EDIT
The point is, if I change the background colour to any value, the area near home button remains semi-transparent while scrolling.
FIXED
func tableView(_ tableView: UITableView, cellForRowAt path: IndexPath) -> UITableViewCell {
let cell = table.dequeueReusableCell(withIdentifier: "identifier", for: path)
let data = ...
cell.textLabel!.text = ...
// here it comes
cell.contentView.superview?.backgroundColor = UIColor.clear
return cell
}
I am having an issue when scrolling in a collection view cell. The text isn't resizing to fit based on the populated value and space available. In the image that isn't working as expected, I have scrolled down a "page" or 2. In the set-up below, extraDetail1 seems to scales based on the first scale size of the first cell created. It appears that when the cell is re-used, the text doesn't resize. I want "extraDetail1" to reduce font size when needed. I am even okay if extraDetail1 truncates if that is my only choice. I don't want the text to overlap. I prefer to have "extraDetail2" = "$..." not reduce in size, but I am okay if it also reduces similar to extraDetail1 if needed.
// cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let actionFigure = headerArray[indexPath.section].sectionActionFigureArray[indexPath.row]
if isShowPhotoView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kPhotoCollectionCellIdentifier, for: indexPath) as! ImageCollectionCell
cell.configureForPhotoCollectionCell(forActionFigure: actionFigure)
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kDetailCollectionCellIdentifier, for: indexPath) as! DetailCollectionCell
cell.configureForDetailCollectionCell(forActionFigure: actionFigure, forFigureCondition: ebaySearchConditionSelected)
return cell
}
}
view layout
cell setup
configuration
what shows up, green is correct, red isn't. I have the same issue with the other text boxes. See that the PDT-8 title line runs off too when its too long.
I have a UICollectionView that is populated with all photos from the devices photo library. After a cell (photo) is tapped, it segues to a view controller that allows for editing. On this view, there is an "Add Photo" button to return the user back to the UICollectionView (to select another photo). I need for the scroll position to "focus" the previous tapped cell in the center of the view without any animations or jumping.
I have tried saving the tapped indexPath as a variable, then on viewDidAppear, scroll to that indexPath with scrollToItemAtIndexPath. The problem is I can't figure out how to update a variable (to save indexPath) on cell tap. I tried this in didSelectItemAtIndexPath, but the value never actually saves.
var cellTappedIndexPath = Int()
Inside didSelectItemAtIndexPath:
cellTappedIndexPath = indexPath.row
The value for cellTappedIndexPath never saves.
Just for testing out scrollToItemAtIndexPath, I have added the following to viewDidAppear:
customViewCollectionView.scrollToItemAtIndexPath(NSIndexPath(forItem: 25, inSection: 0), atScrollPosition: UICollectionViewScrollPosition.CenteredVertically, animated: false)
// 25 is just a number I have set for testing. Ultimately, I would like this to be the saved indexPath of the last tapped cell.
This causes the collectionView to "jump" to cell 25 once it's fully loaded. If I set animated to true, it loads at the top, then scrolls down to cell 25. Not my desired result.
I just want to be able to do 2 things here.
1 - Save the cell tapped as a variable.
2 - use scrollToItemAtIndexPath (with the variable in #1) so the view just loads up instantly with the last cell tapped right into the middle, No animations or anything.
Let me know if further clarification is needed. THANKS!
You could save the selected indexPathreceived when the collectionview cell is tapped and use it when required.
var selectedIndexPath: NSIndexPath?
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedIndexPath = indexPath
}
func scrollToCell(){
if let index = selectedIndexPath{
customViewCollectionView.scrollToItemAtIndexPath(index, atScrollPosition: .CenteredVertically, animated: false)
}else{
print("A cell hasnt been selected yet")
}
}