tvOS - UICollectionViewCompositionalLayout & NSLayoutConstraint issue

13 views Asked by At

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")
    }
}
0

There are 0 answers