I'm trying to implement a menu in two states, something similar to the YouTube app. When open, I try to show text and an icon, and when closed, only the icon. When pressing the button on the remote - pressesBegan(), I update the view sizes and get an error
*** Assertion failure in -[UICollectionView _updateLayoutAttributesForExistingVisibleViewsFadingForBoundsChange:], UICollectionView.m:6218 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView internal inconsistency: missing final attributes for cell <arlnc_tvOS.AppCell: 0x10550a2b0; baseClass = UICollectionViewCell; frame = (80 0; 250 70); focused = YES; layer = <CALayer: 0x60000025b180>>; initial attributes: <UICollectionViewLayoutAttributes: 0x105612a40; index path: (0-0); frame = (80 0; 250 70)>; layout query: (null); collection view: <UICollectionView: 0x105815a00; frame = (0 0; 70 1080); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000c69bf0>; layer = <CALayer: 0x600000203fe0>; contentOffset: {0, -60}; contentSize: {70, 0}; adjustedContentInset: {60, 0, 60, 0}; layout: <UICollectionViewCompositionalLayout: 0x107905420>; dataSource: <arlnc_tvOS.AppController: 0x10570bfd0>>; data source counts: [(0:5)]'
full code
class AppController: UIViewController {
lazy var collectionView: UICollectionView = {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(70))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
return UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewCompositionalLayout(section: section))
}()
weak var sidebarWidth: NSLayoutConstraint?
private(set) var isSidebarOpened = true
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.register(cell: AppCell.self)
collectionView.dataSource = self
collectionView.constraints(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.bottomAnchor, trailing: nil)
sidebarWidth = collectionView.widthAnchor.constraint(equalToConstant: 330)
sidebarWidth?.isActive = true
}
func pressesBegan() {
DispatchQueue.main.async {
self.isSidebarOpened = !self.isSidebarOpened
if self.isSidebarOpened {
self.sidebarWidth?.constant = 330
self.collectionView.frame = CGRect(x: 0, y: 0, width: 330, height: UIScreen.main.bounds.height)
} else {
self.sidebarWidth?.constant = 70
self.collectionView.frame = CGRect(x: 0, y: 0, width: 70, height: UIScreen.main.bounds.height)
}
UIView.animate(withDuration: 0.3, delay: 0, options: .layoutSubviews, animations: {
self.view.layoutIfNeeded()
}) { _ in
self.collectionView.reloadData()
}
}
}
}
UICollectionViewDataSource
extension AppController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withClass: AppCell.self, for: indexPath)
cell.name.isHidden = !isSidebarOpened
cell.name.text = "index \(indexPath.row)"
cell.icon.image = UIImage(systemName: "house")
return cell
}
}
UICollectionViewCell
class AppCell: UICollectionViewCell {
lazy var icon = UIImageView()
let name = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubviews(icon, name)
icon.constraints(top: topAnchor, leading: leadingAnchor, bottom: nil, trailing: nil, padding: .init(top: 17.5, left: 17.5, bottom: 0, right: 0), size: .init(width: 35, height: 35))
name.constraints(top: topAnchor, leading: icon.trailingAnchor, bottom: nil, trailing: nil, padding: .init(top: 17.5, left: 17.5, bottom: 0, right: 0), size: .init(width: 0, height: 35))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}