I wanted to make a UI like this.
The component of the chevron down image on the right side of the cell is the UICellAccessory
that I customized. From now on, I will call this moreAccessory
.
To make this UI, I configured the dataSource
with redCellRegistration
and blueCellRegistration
as follows.
let redCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .red
cell.contentConfiguration = content
let image = UIImageView(image: UIImage(systemName: "chevron.down"))
let configuration = UICellAccessory.CustomViewConfiguration(customView: image,
placement: .trailing(),
tintColor: .systemGray)
let moreAccessory = UICellAccessory.customView(configuration: configuration)
cell.accessories = [moreAccessory]
}
let blueCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .blue
cell.contentConfiguration = content
let image = UIImageView(image: UIImage(systemName: "chevron.down"))
let configuration = UICellAccessory.CustomViewConfiguration(customView: image,
placement: .trailing(),
tintColor: .systemGray)
let moreAccessory = UICellAccessory.customView(configuration: configuration)
cell.accessories = [moreAccessory]
}
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
if indexPath.row.isMultiple(of: 2) {
return collectionView.dequeueConfiguredReusableCell(using: redCellRegistration, for: indexPath, item: identifier)
} else {
return collectionView.dequeueConfiguredReusableCell(using: blueCellRegistration, for: indexPath, item: identifier)
}
}
And I recognized that there was a duplicate code for creating moreAccessory
instance in redCellRegistration
and blueCellRegistration
, so decided to make this accessory a single variable and use it in common.
let moreAccessory: UICellAccessory = {
let image = UIImageView(image: UIImage(systemName: "chevron.down"))
let configuration = UICellAccessory.CustomViewConfiguration(customView: image,
placement: .trailing(),
tintColor: .systemGray)
let accessory = UICellAccessory.customView(configuration: configuration)
return accessory
}()
let redCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .red
cell.contentConfiguration = content
cell.accessories = [moreAccessory]
}
let blueCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .blue
cell.contentConfiguration = content
cell.accessories = [moreAccessory]
}
But the code above caused hang.
When looking at the Instruments tool, UICellAccessoryManager_updateAccessories:prevailingAccessories:withLayout:edge:
function occupied too much weight.
The usage of cpu was 100%, and the memory usage also continued to go up.
However, if I create and use an accessory through a function that returns moreAccessory
, there was no error in this situation.
func moreAccessory() -> UICellAccessory {
let image = UIImageView(image: UIImage(systemName: "chevron.down"))
let configuration = UICellAccessory.CustomViewConfiguration(customView: image,
placement: .trailing(),
tintColor: .systemGray)
let accessory = UICellAccessory.customView(configuration: configuration)
return accessory
}
let redCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .red
cell.contentConfiguration = content
cell.accessories = [moreAccessory()]
}
let blueCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .blue
cell.contentConfiguration = content
cell.accessories = [moreAccessory()]
}
I thought, "Well, was it a problem to use the same instance?". And assigned moreAccessory
to the variables in redCellRegistration
and blueCellRegistration
separately. (Because the UICellAccessory
is struct, if I assign it to a variable, the instance will be copied.)
let moreAccessory: UICellAccessory = {
let image = UIImageView(image: UIImage(systemName: "chevron.down"))
let configuration = UICellAccessory.CustomViewConfiguration(customView: image,
placement: .trailing(),
tintColor: .systemGray)
let accessory = UICellAccessory.customView(configuration: configuration)
return accessory
}()
let redCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .red
cell.contentConfiguration = content
let redCellAccessory = moreAccessory
cell.accessories = [redCellAccessory]
}
let blueCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
content.textProperties.color = .blue
cell.contentConfiguration = content
let blueCellAccessory = moreAccessory
cell.accessories = [blueCellAccessory]
}
However, there was an error at this time, too.
And If I make moreAccessory
as a predefined accessory such as disclosureIndicator
, not customView
, there was no error.
let moreAccessory: UICellAccessory = {
let accessory = UICellAccessory.disclosureIndicator()
return accessory
}()
UICellAccessory
is a struct, but the UIView
subclass it contains when creating a custom accessory is a class. When the struct is copied, the same view instance will be used; a new view will not be created. You cannot share the same single custom view (a UIImageView
in your example) between cells.
You have already found the correct solution: you need to make a new instance of UICellAccessory
for every separate row if you are using a custom view. Your original code worked, but to avoid the duplication you can have a factory method to create a new cell accessory. That was your other solution that worked because it was creating a new instance each time it was called.