I'm encountering an issue with system accessories in UICollectionViewCells after overriding the trait collection. Specifically, the accessories are misaligned and shifted downwards.
This issue occurs when using setOverrideTraitCollection (other trait override methods produce the same result). Interestingly, this doesn't happen with all accessories; for example, the disclosureIndicator is scaled correctly.
If I don't use setOverrideTraitCollection (or other trait override methods), the system accessories scale as expected.
Here's a code snippet demonstrating the issue.
I have a "Fix Size" button that overrides the trait collection to a UITraitCollection with a UIContentSizeCategory of large. The "Follow Settings" button resets the trait collection, allowing the views to scale according to the system settings.
import UIKit
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Fix Size", for: .normal)
return button
}()
let buttonRe: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Follow Settings", for: .normal)
return button
}()
var listItems: [Int] = [1, 2, 3, 4, 5]
var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(
self,
action: #selector(ViewController.buttonTapped),
for: .touchUpInside
)
buttonRe.addTarget(
self,
action: #selector(ViewController.buttonTappedToRe),
for: .touchUpInside
)
view.backgroundColor = .white
view.addSubview(button)
view.addSubview(buttonRe)
setupCollectionView()
if let collectionView = collectionView {
view.addSubview(collectionView)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(
x: 0,
y: 0,
width: view.bounds.width,
height: view.bounds.height - 100
)
button.frame = CGRect(
x: 0,
y: view.bounds.height - 100,
width: view.bounds.width / 2,
height: 50
)
buttonRe.frame = CGRect(
x: view.bounds.width / 2,
y: view.bounds.height - 100,
width: view.bounds.width / 2,
height: 50
)
}
@objc func buttonTapped() {
setOverrideTraitCollection(
UITraitCollection(preferredContentSizeCategory: .large),
forChild: self
)
}
@objc func buttonTappedToRe() {
setOverrideTraitCollection(nil,forChild: self)
}
private func updateCollectionViewLayout() {
guard let collectionView = collectionView else { return }
collectionView.collectionViewLayout.invalidateLayout()
collectionView.performBatchUpdates(nil)
collectionView.reloadData()
}
private func setupCollectionView() {
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
config.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath in
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] _, _, completion in
self?.deleteItem(at: indexPath)
completion(true)
}
return UISwipeActionsConfiguration(actions: [deleteAction])
}
let layout = UICollectionViewCompositionalLayout.list(using: config)
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewListCell.self, forCellWithReuseIdentifier: "cell")
collectionView.isEditing = true
self.collectionView = collectionView
}
private func deleteItem(at indexPath: IndexPath) {
listItems.remove(at: indexPath.item)
guard let collectionView = collectionView else { return }
collectionView.deleteItems(at: [indexPath])
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory {
updateCollectionViewLayout()
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return listItems.count }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewListCell
var content = UIListContentConfiguration.valueCell()
content.text = "Item \(listItems[indexPath.item])"
cell.contentConfiguration = content
cell.accessories = [.delete(
options: UICellAccessory.DeleteOptions(
reservedLayoutWidth: .custom(50)
)
)]
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
}
}
The attached screenshot illustrates the misalignment that occurs after tapping the 'Fix Size' button, with the system accessibility text size set to accessibilityExtraExtraExtraLarge setting
Has anyone else experienced this issue or have suggestions on how to resolve it? Any help would be greatly appreciated!