Please run the following UIKit app.
It displays a collection view with compositional layout (list layout) and diffable data source.
import UIKit
class ViewController: UIViewController {
var bool = false {
didSet {
var snapshot = dataSource.snapshot()
snapshot.reconfigureItems(snapshot.itemIdentifiers)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<String, String>!
var snapshot: NSDiffableDataSourceSnapshot<String, String> {
var snapshot = NSDiffableDataSourceSnapshot<String, String>()
snapshot.appendSections(["section"])
snapshot.appendItems(["id"])
return snapshot
}
override func viewDidLoad() {
super.viewDidLoad()
configureHierarchy()
configureDataSource()
}
func configureHierarchy() {
collectionView = .init(frame: view.bounds, collectionViewLayout: createLayout())
view.addSubview(collectionView)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func createLayout() -> UICollectionViewLayout {
let configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
return UICollectionViewCompositionalLayout.list(using: configuration)
}
func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { [weak self] cell, indexPath, itemIdentifier in
guard let self else { return }
let _switch = UISwitch()
cell.accessories = [
.customView(configuration: .init(
customView: _switch,
placement: .trailing())
),
// .disclosureIndicator()
]
_switch.isOn = bool
_switch.addTarget(self, action: #selector(toggleBool), for: .valueChanged)
}
dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)
}
dataSource.apply(self.snapshot, animatingDifferences: false)
}
@objc func toggleBool() {
bool.toggle()
}
}
When you tap on the switch, it lags.
If you uncomment .disclosureIndicator()
and tap on the switch, it doesn't lag.
How do I make it so that the switch doesn't lag without having a disclosure indicator in the cell?
Note: while it would solve the issue, I would prefer not to declare the switch at the class level, as I don't want to declare all my controls, which could be quite a lot, at the view controller level in my real app.
Edit: declaring the switch at the configureDataSource() level also fixes it, but it would still be inconvenient to declare many switches, say of a list with n elements, at that level.
Make a custom cell:
class SwitchCell: UICollectionViewListCell {
let _switch = UISwitch()
override init(frame: CGRect) {
super.init(frame: frame)
accessories = [
.customView(configuration: .init(
customView: _switch,
placement: .trailing())
)
]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And edit cellRegistration
accordingly:
let cellRegistration = UICollectionView.CellRegistration<SwitchCell, String> { [weak self] cell, indexPath, itemIdentifier in
guard let self else { return }
cell._switch.isOn = bool
cell._switch.addTarget(self, action: #selector(toggleBool), for: .valueChanged)
}