The behavior of the UICollectionViewFlowLayout is not defined because


#1

The behavior of the UICollectionViewFlowLayout is not defined because:

the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.

Эта ошибка возникает при повороте экране. Уже который день мучаюсь с этой ошибкой. Весь интернет уже обшарил… Прикол в том, что ширина точно не выходит за рамки… и почему-то это происходит только с одной коллекцией, а с другими коллекциями все нормально. Так о чем это говорит все же?

Вот фул код проекта (создал отдельным проектом):

import UIKit

class MainViewController: UIViewController {

    let profileButtons = ProfileButtons(collectionViewLayout: UICollectionViewLayout())
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setButtonsCollection()
    }
    
    private func setButtonsCollection() // меню
    {
        addChild(profileButtons)
        view.addSubview(profileButtons.view)
        profileButtons.didMove(toParent: self)
        
        profileButtons.view.translatesAutoresizingMaskIntoConstraints = false
        profileButtons.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
        profileButtons.view.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        profileButtons.view.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        profileButtons.view.heightAnchor.constraint(equalToConstant: profileButtons.cvHeight).isActive = true
    }
}


import UIKit

struct ProfileButtonsSection {
    let title: String
    let items: [ProfileButtonsItems]
}

struct ProfileButtonsItems {
    let isNav: Bool
    let name: String
    let image: UIImage
}

class ProfileButtonsFlowLayout: UICollectionViewFlowLayout {

//    override func shouldInvalidateLayout(forBoundsChange: CGRect) -> Bool
//    {
//        super.shouldInvalidateLayout(forBoundsChange: forBoundsChange)
//
//        return false
//    }
}

class ProfileButtons: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    
    public let flowLayout = ProfileButtonsFlowLayout()
    
    private let sections: [ProfileButtonsSection] = [
        ProfileButtonsSection(
            title : "Аккаунт",
            items : [
                ProfileButtonsItems(isNav : false, name: "Тестовое название", image: #imageLiteral(resourceName: "wallet")),
                ProfileButtonsItems(isNav : true, name: "Тестовое название", image: #imageLiteral(resourceName: "clock")),
                ProfileButtonsItems(isNav : false, name: "Тестовое название", image: #imageLiteral(resourceName: "coeffFormat")),
            ]
        ),
        ProfileButtonsSection(
            title : "Безопасность",
            items : [
                ProfileButtonsItems(isNav : true, name: "Изменить пароль", image: #imageLiteral(resourceName: "pass")),
                ProfileButtonsItems(isNav : true, name: "Двухфакторная аутентификация", image: #imageLiteral(resourceName: "protect")),
            ]
        ),
        ProfileButtonsSection(
            title : "Помощь",
            items : [
                ProfileButtonsItems(isNav : true, name: "Служба поддержки", image: #imageLiteral(resourceName: "support")),
                ProfileButtonsItems(isNav : true, name: "Правила и условия", image: #imageLiteral(resourceName: "list")),
                ProfileButtonsItems(isNav : true, name: "Тестовое название", image: #imageLiteral(resourceName: "license")),
            ]
        ),
        ProfileButtonsSection(
            title : "",
            items : [
                ProfileButtonsItems(isNav : false, name: "Тестовое название", image: #imageLiteral(resourceName: "star2")),
                ProfileButtonsItems(isNav : false, name: "Выход", image: #imageLiteral(resourceName: "exit")),
            ]
        ),
    ]
    
    // имя переиспользуемого заголовка/подвала
    private let headerId = "header"
    private let footerId = "footer"
    
    // имя переиспользуемой ячейки
    private let cellId = "cell"

    // высота ячейки
    private let cellHeight: CGFloat = 40
    // высота заголовка секции
    private let headerHeight: CGFloat = 40
    
    // высота футера секции
    private let footerHeight: CGFloat = 20
    
    // высота коллекции
    public var cvHeight: CGFloat {
        
        var itemsCount: Int = 0
        
        for (value) in sections
        {
            itemsCount += value.items.count
        }
        
        let sectionCount = sections.count - 1 // не учитывать последнюю секции
        
        let cvHeight =  ( Int(headerHeight) * sectionCount ) + (Int(cellHeight) * itemsCount) + (Int(footerHeight) * sectionCount)
        
        return CGFloat(cvHeight)
    }
    
    var width: CGFloat!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // минимальное расстояние между ячейками (по основной оси)
        flowLayout.minimumInteritemSpacing = 0
        // минимальное расстояние между ячейками (по поперечной оси)
        flowLayout.minimumLineSpacing = 0
        // отступы для секций
        flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        // направление заполнения ячеек
        flowLayout.scrollDirection = .vertical
        //flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        //flowLayout.itemSize = CGSize(width: collectionView.frame.width, height: cellHeight)
        
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collectionView.register(ProfileButtonCell.self, forCellWithReuseIdentifier: cellId)
        collectionView.register(ProfileButtonSectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerId)
        collectionView.register(ProfileButtonSectionFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footerId)
        collectionView.backgroundColor = .clear
        
        width = collectionView.frame.width
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        //print(collectionView.frame.width, size.width)
        
        //flowLayout.itemSize.width = size.width
        width = size.width
        print("viewWillTransition")
        flowLayout.invalidateLayout()
    }

    // кол-во секций
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        
        return sections.count
    }

    // кол-во ячеек в секции
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        return sections[section].items.count
    }
    
    // возвращает секцию
//    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
//      switch kind {
//        case UICollectionView.elementKindSectionHeader:
//
//            let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath as IndexPath) as! ProfileButtonSectionHeader
//
//            let section = sections[indexPath.section]
//
//            header.label.text = section.title.uppercased()
//
//            return header
//
//        case UICollectionView.elementKindSectionFooter:
//            let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerId, for: indexPath as IndexPath)
//
//            return footer
//
//        default:
//           return UICollectionReusableView()
//        }
//    }
    
    // возвращает ячейку
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ProfileButtonCell
        
        let item = sections[indexPath.section].items[indexPath.row]

        if !item.isNav
        {
            cell.imageNavNext.isHidden = true
        }
        cell.image.image = item.image
        cell.label.text = item.name
        
        return cell
    }
    
    // размер секции заголовка
//    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
//
//        return sections.count-1 == section ? CGSize(width: 0, height: 0) : CGSize(width: collectionView.frame.width, height: headerHeight)
//    }
//
//    // размер секции футера
//    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
//
//        return sections.count-1 == section ? CGSize(width: 0, height: 0) : CGSize(width: collectionView.frame.width, height: footerHeight)
//    }
//
    // размер ячейки
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        print("sizeForItemAt")
        return CGSize(width: width, height: cellHeight)
    }
}

// верхний колонтитул секции
class ProfileButtonSectionHeader: UICollectionReusableView {
  
    let label: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .systemGray
        label.font = UIFont(descriptor: UIFontDescriptor.init(name: "Google Sans Regular", size: 0), size: 12)
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(label)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
    
        label.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        label.leftAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leftAnchor, constant: 20).isActive = true
        label.rightAnchor.constraint(equalTo: self.safeAreaLayoutGuide.rightAnchor, constant: 0).isActive = true
        label.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
    }

}

// нижний колонтитул секциии
class ProfileButtonSectionFooter: UICollectionReusableView {
  
    let borderTop: UIView = {
        let borderTop = UIView()
        borderTop.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.1)
        borderTop.translatesAutoresizingMaskIntoConstraints = false
        return borderTop
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(borderTop)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        borderTop.leftAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leftAnchor, constant: 20).isActive = true
        borderTop.rightAnchor.constraint(equalTo: self.safeAreaLayoutGuide.rightAnchor, constant: -20).isActive = true
        borderTop.heightAnchor.constraint(equalToConstant: 1).isActive = true
        borderTop.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
    }

}

// ячейка
class ProfileButtonCell: UICollectionViewCell {
    
    override var isHighlighted: Bool {
        didSet {
            let bgColor = isHighlighted ? UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.1) : UIColor.clear
            self.backgroundColor = bgColor
        }
    }
    
    let label: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .white
        label.font = UIFont(descriptor: UIFontDescriptor.init(name: "Google Sans Regular", size: 0), size: 14)
        return label
    }()
    
    let image: UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.contentMode = .scaleAspectFit
        image.tintColor = .gray
        return image
    }()
    
    let imageNavNext: UIImageView = {
        let image = UIImageView()
        image.image = #imageLiteral(resourceName: "nav-next")
        image.translatesAutoresizingMaskIntoConstraints = false
        image.contentMode = .scaleAspectFit
        image.tintColor = .gray
        return image
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(image)
        contentView.addSubview(label)
        contentView.addSubview(imageNavNext)
        
        image.widthAnchor.constraint(equalToConstant: 17).isActive = true
        image.heightAnchor.constraint(equalToConstant: 17).isActive = true
        image.leftAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leftAnchor, constant: 20).isActive = true
        image.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true

        label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
        label.leftAnchor.constraint(equalTo: image.leftAnchor, constant: 30).isActive = true
        label.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true

        imageNavNext.widthAnchor.constraint(equalToConstant: 11).isActive = true
        imageNavNext.heightAnchor.constraint(equalToConstant: 11).isActive = true
        imageNavNext.rightAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.rightAnchor, constant: -20).isActive = true
        imageNavNext.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Текущий код, который я скинул, работает для iphone 8 и ниже, но с ворнингом:

[App] if we’re in the real pre-commit handler we can’t actually add any new fences due to CA restriction

Для iphone 10 и выше не работает…

Но хочу заметить, что работает все корректно! Читал, что эти предупреждения или ошибки безвредны… но все же… почему тогда они не возникают в других коллекциях?