I'm a fairly new developer and I have some long cellForItemAt methods. I have a feeling I'm missing something important.
In this ViewController I have a segmented control that filters data with a boolean taskView
property.
Here's what my cellForItemAt call looks like:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TaskCell.reuseIdentifier, for: indexPath) as! TaskCell
//SwipeCell Delegate
cell.delegate = self
var transactionResults: Results<Transaction>
if taskView {
transactionResults = unclearedTransactionsToDate
} else {
transactionResults = allTransactions
}
cell.configureCollectionViewCells(indexPath, transactionResults)
let balanceAtDate: Double = realm.objects(Transaction.self).filter("transactionDate <= %@", transactionResults[indexPath.item].transactionDate).sum(ofProperty: "transactionAmount")
cell.balanceLabel.attributedText = balanceAtDate.toAttributedString(size: 9, offset: 6)
if transactionResults[indexPath.item].isCleared == false && !taskView {
cell.amountLabel.textColor = .lightGray
cell.subcategoryLabel.textColor = .lightGray
cell.dateLabel.textColor = .lightGray
cell.balanceLabel.textColor = .lightGray
cell.circleView.backgroundColor = .lightGray
} else {
cell.subcategoryLabel.textColor = .black
cell.dateLabel.textColor = .black
cell.balanceLabel.textColor = .black
cell.circleView.backgroundColor = UIColor(rgb: transactionResults[indexPath.item].transactionCategory!.categoryColor)
}
return cell
}
}
And in my configureCollectionViewCells
method I have:
func configureCollectionViewCells(_ indexPath: IndexPath, _ transaction: Results<Transaction>) {
imageView.image = UIImage(named: transaction[indexPath.item].transactionCategory!.categoryName)
imageView.tintColor = .white
circleView.backgroundColor = UIColor(rgb: transaction[indexPath.item].transactionCategory!.categoryColor)
subcategoryLabel.textColor = .black
dateLabel.textColor = .black
balanceLabel.textColor = .black
subcategoryLabel.text = transaction[indexPath.item].transactionSubCategory?.subCategoryName
amountLabel.attributedText = transaction[indexPath.item].transactionAmount.toAttributedString(size: 9, offset: 6)
let formatter = DateFormatter()
formatter.dateFormat = "MMMM dd, yyyy"
let dateString = formatter.string(from: transaction[indexPath.item].transactionDate)
dateLabel.text = dateString
if transaction[indexPath.item].transactionAmount > 0 {
amountLabel.textColor = UIColor(rgb: Constants.green)
} else {
amountLabel.textColor = UIColor(rgb: Constants.red)
}
}
The code works, but I have a feeling that it's not being implemented properly. Can someone give me some advice (keeping in mind that I'm new to programming) on how to manage such lengthy functions? I feel like I'm missing a couple of concepts.
Some people have just said "Throw everything in cellForItemAt" and some only have 3 lines in cellForItemAt.
I think maybe I should be overriding the layoutSubviews
method in the collectionView cell and implementing some of the code there.
Any general or specific advice is greatly appreciated. I would also be interested in researching if anyone has any resources on the topic.
Thank you in advance.
What you're doing is not "wrong" in and of itself. However, there is a school of thought which says that, ideally, cellForRowAt
should know nothing of the internal interface of the cell. This (cellForRowAt
) is the data source. It should hand the cell just the data. You have a cell subclass (TaskCell), so it just needs some methods or properties that allow it to be told what the data is, and the cell should then format itself and populate its own interface in accordance with those settings.
If all of that formatting and configuration code is moved into the cell subclass, the cellForRowAt
implementation will be much shorter, cleaner, and clearer, and the division of labor will be more appropriate.
In support of this philosophy, I would just add that Apple has adopted it in iOS 14, where a cell can now have a UIContentConfiguration object whose job is to communicate the data from cellForRowAt
into the contentView
of the cell. So for example instead of saying (for a table view cell) cell.textLabel.text = "howdy"
you say configuration.text = "howdy"
and let the configuration object worry about the fact that a UILabel might be involved in the interface.