I am using UICollectionFlowLayout to establish minimumLineSpacing between collectionItems, I am trying to find a way to set the spacing to zero for some cells so there is no spacing, i.e they appear to be 'merged' while leaving others with their spaces intact, is it possible to make alterations in the cellForItemAt method for example to achieve this?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customColCell", for: indexPath) as! customColCell
if indexPath.row == 5 {
self.minimumLineSpacing = 100 // -- this does not result in an individual change...
}
cell.textLabel.text = indexPath.row.description
return cell
}
is it possible to make alterations in the cellForItemAt method for example to achieve this
No. What you're asking to do is not how a UICollectionViewFlowLayout behaves by default. You will need to write a collection view layout subclass.
Related
In my view controllers, a lot of my cellForItemAt methods have computations in them. In the following example, I get the amount, image, and balanceAtDate and pass them into my view:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SummaryCell.cellId, for: indexPath) as! SummaryCell
let amount = filteredTransactions[indexPath.row].transactionAmount
let image = UIImage(named: filteredTransactions[indexPath.item].transactionCategory!.categoryName)
let balanceAtDate: Double = realm.objects(Transaction.self).filter("transactionDate <= %#", transactionResults[indexPath.item].transactionDate).sum(ofProperty: "transactionAmount")
cell.configure(with: image, and: amount, and: balanceAtDate)
return cell
}
It has come to my attention that I shouldn't make any computations or calculations in cellForItemAt because it is called for every cell and it will slow down the application.
I create 3 constants: amount, image, and balanceAtDate. Do I need to put all three somewhere else, or just the balanceAtDate?
Where, then, do I put a computation that requires the indexPath.item to get something like the balanceAtDate?
so I am trying to change the background color of the collection view cell that is clicked, however, when I call the CollectionView.reloadData() method the backgrounds don't change. I am thinking that reloading the data only checks for adding and deleting cells, but does not actually call the configure function for each cell again. I was wondering how I can reconfigure these cells so that the background color can change for the selected cell. Thanks for the help in advance!
This is the method that I call:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
...
bagCollectionView.reloadData()
}
Instead of calling reloadData, you can try:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = yourColor
}
Even though you do your logic in did select by finding a cell or implement logic in didSelectItemAt and didDeSelectItemAt, I suggest you should store the Index. Due to reusability of cell, there is chance that selected cell do not show color on scroll or if you have large number or records.
So save index in array(for multiple selections) or a single variable(single selection) and manage logic for the same.
For example:
cell.backgroundColor = selectedIndex == indexPath.item ? .red : .yellow
In my project, I have a UICollectionView. In the UICollectionView, I have a custom cell.
I am able to print the cell value when it is selected within "didSelectItemAt", however, if I try to edit the cell in any way within this method, it does not change.
I'm sure I'm missing something, any help would be appreciated!
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return statValues.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
cell.statLabel.text = statHeaders[indexPath.row]
cell.statLabel.textColor = UIColor(red:0.31, green:0.31, blue:0.31, alpha:1.0)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
print(cell.statLabel.text)
cell.backgroundColor = UIColor.yellow
collectionView.reloadData()
}
When user selects a cell, the code is correctly printing the value of the indexPath, however the backgroundColor does not change.
My guess would be that you are creating a new instance of cell instead of using the one in the collectionView
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Change this line
let cell = collectionView.cellForItemAtIndexPath(indexPath: indexPath)
print(cell.statLabel.text)
cell.backgroundColor = UIColor.yellow
collectionView.reloadData()
}
Also, you should probably keep an external data model for your source of truth. If you have enough collectionViews that requires scrolling, when you scroll offscreen, your cells will be reused in a random order causing cells that you did not click to be yellow.
Create a seperate array such as
var selectedStatHeaders: Set<Int>()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
cell.statLabel.text = statHeaders[indexPath.row]
cell.statLabel.textColor = UIColor(red:0.31, green:0.31, blue:0.31, alpha:1.0)
// Reset/configure cell each reload
if selectedStatHeaders.contains(indexPath.row) { // Can also make this into a ternary
cell.backgroundColor = UIColor.yellow
} else {
cell.backgroundColor = UIColor.whit
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedStatHeaders.insert(indexPath.row)
collectionView.reloadItemsAtIndexPath(indexPath: indexpath)
}
Hmm... Doesn't make sense if the code is able to print but the background doesn't change color. Do you mean changing back from yellow to white? Anyway, just a hunch but I suspect it's because you are calling collectionView.reloadData() after your set the backgroundColor change.
https://developer.apple.com/documentation/uikit/uicollectionview/1618078-reloaddata
This causes the collection view to discard any currently visible
items (including placeholders) and recreate items based on the current
state of the data source object.
I use this code to show a collection of images, which are scrollable. However, I would like to add a property to make it possible to identify each cell or image in this case. I want a property to every cell that can hold a string.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PalletsCollectionViewCell", for: indexPath) as! PalletsCollectionViewCell
cell.imgImage.image = imageArray[indexPath.row];
return cell;
}
Haven't found anything while reading the swift documentation.
What I want to do is to add events to the cells. If the user touches a cell, a function will be called. This function performs an API request with the property value as parameter.
There are a few posts on UICollectionViews and as a general concept I understand UICollectionViews are similar to how TableViews are implemented. There are a few posts on this topic already.
How to make a simple collection view with Swift
I'm using a UICollectionView to layout 8 cells for essentially decorative purposes...just as a container to hold a few images for a page. The images aren't meant to be interactive eg. a shopping cart.
My question is whether I need to implement the function below (and other similar functions) if i'm laying out my cells on the storyboard.
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
I have 8 cells within a UICollectionView that i have configured on the storyboard (this collection view is a view on a UIViewController) and each UICollectionViewCell has an image view with in. I have laid this all out on the storyboard.
I also don't want to put an image in each cell. Just a certain number of cels eg. cells 2,3,5 and 8.
So what I was going to do is create a class that extends UIControllerViewCell and create an IBOutlet for the image view and then in the interface builder reference the custom cell class.
And I think - but could do with some advice - then all I need to do is use the function below and because I only want to populate certain cells I can use an if statement for index path when it's 2,3,5 and 8:
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(indexPath == 2 || 3 || 5|| 8)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.image = self.items[indexPath.item]
return cell
}
}
Thanks.
cellForItemAt must return value (which is UICollectionViewCell instance in this case), but you are free to set image of certain cells to nil (AKA no image)
eg:
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCollectionViewCell
if([2,3,5,8].contains(indexPath.row))
{
// Use the outlet in our custom class to get a reference to the UIImageView in the cell
cell.myImageView.image = self.items[indexPath.row]
} else {
cell.myImageView.image = nil
}
return cell
}
Sounds like you are asking about a static UICollectionViewController which is not possible. You will make a single prototype cell in the storyboard and link the IBOutlets with the desired class. You will also need to implement the data source and delegate methods for UICollectionView. Just hide the image where you don't want to show it based on the index.