当项目大小是自动的并且显示标题时,UICollectionView 崩溃

Posted

技术标签:

【中文标题】当项目大小是自动的并且显示标题时,UICollectionView 崩溃【英文标题】:UICollectionView crashes when item size is automatic and header is shown 【发布时间】:2021-12-28 03:36:26 【问题描述】:

当我想将UICollectionViewestimatedItemSize 属性设置为UICollectionViewFlowLayout.automaticSize 并同时显示一个节标题时,我遇到了一个奇怪的错误。

使用下面的代码,应用程序可以正确显示UICollectionView。但是,一旦用户在UICollectionView 上滚动,应用程序会因递归调用函数updateVisibleCellsNow 而崩溃。

通过将estimatedItemSizeautomaticSize 设置为none,我从另一个 *** 问题中找到了解决方法。但是,我想将自动布局功能设置为UICollectionViewCell,而不是自己计算单元格高度。有没有更好的解决方案?谢谢。

这是我关于 ViewController 的代码

import UIKit
class DemoCollectionViewController: UIViewController 
    
    lazy private var collectionView: UICollectionView =  [weak self] in
        guard let strongSelf = self else  return UICollectionView() 
        
        // Setup of `UICollectionViewFlowLayout`
        let flowLayout = UICollectionViewFlowLayout()
        

        // ********* This is the line that causes crash *********
        flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        // ******************************************************

        flowLayout.headerReferenceSize = CGSize(width: getScreenWidth(), height: 44.0)
        flowLayout.sectionHeadersPinToVisibleBounds = true
        
        // Setup of `UICollectionView`
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout).disableFrameToAutoLayout()
        collectionView.dataSource = strongSelf
        collectionView.register(ProfileThumbnailCollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
        collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerId")
        
        return collectionView
    ()
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        // Setup of the UICollectionView
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            collectionView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
        ])
    


extension DemoCollectionViewController: UICollectionViewDataSource 
    func numberOfSections(in collectionView: UICollectionView) -> Int 
        return 10
    
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return 20
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! ProfileThumbnailCollectionViewCell
        cell.contentView.backgroundColor = .yellow
        return cell
    
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 

        switch kind 
        case UICollectionView.elementKindSectionHeader:
            let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerId", for: indexPath)
            supplementaryView.backgroundColor = .red
            return supplementaryView
        default:
            return UICollectionReusableView()
        
    


这是我关于ProfileThumbnailCollectionViewCell的代码:

class ProfileThumbnailCollectionViewCell: UICollectionViewCell 
    
    private let profileThumbnailImageView: UIImageView = 
        let profileThumbnailImageView = UIImageView()
        profileThumbnailImageView.translatesAutoresizingMaskIntoConstraints = false
        profileThumbnailImageView.backgroundColor = .red
        profileThumbnailImageView.layer.cornerRadius = 60
        profileThumbnailImageView.layer.masksToBounds = true
        return profileThumbnailImageView
    ()
    
    private let editPenButton: UIButton = 
        let editPenButton = UIButton()
        editPenButton.translatesAutoresizingMaskIntoConstraints = false
        editPenButton.backgroundColor = .mainGreen
        editPenButton.layer.cornerRadius = 16
        editPenButton.layer.masksToBounds = true
        return editPenButton
    ()
    
    override init(frame: CGRect) 
        super.init(frame: frame)
        
        // Content View
        contentView.backgroundColor = .yellow
        NSLayoutConstraint.activate([
            contentView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width)
        ])
        
        // Profile Thumbnail ImageView
        contentView.addSubview(profileThumbnailImageView)
        NSLayoutConstraint.activate([
            profileThumbnailImageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            profileThumbnailImageView.widthAnchor.constraint(equalToConstant: 120),
            profileThumbnailImageView.heightAnchor.constraint(equalToConstant: 120),
            profileThumbnailImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 25),
            profileThumbnailImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -25)
        ])
        
        // Edit thumbnail pen ImageView
        contentView.addSubview(editPenButton)
        NSLayoutConstraint.activate([
            editPenButton.widthAnchor.constraint(equalToConstant: 32),
            editPenButton.heightAnchor.constraint(equalToConstant: 32),
            editPenButton.rightAnchor.constraint(equalTo: contentView.rightAnchor),
            editPenButton.rightAnchor.constraint(equalTo: contentView.rightAnchor)
        ])
    
    
    required init?(coder: NSCoder) 
        fatalError("This class should be inited from code instead of a nib file; init(coder:) has not been implemented")
    


【问题讨论】:

updateVisibleCellsNow 中有什么。你能分享完整的代码吗.. 嗨,updateVisibleCellsNowUICollectionView 的私有函数。因此,我无法访问代码。 我有一个类似的问题,并注意到将 sectionHeadersPinToVisibleBounds 设置为 false 可以修复崩溃。暂时没有找到替代品。 编辑:切换到 CompositionalLayout 似乎可以解决我的问题 【参考方案1】:

在 ViewDidLoad 中调用 registerNib

func registerNib()
 let nib = UINib(nibName: "ProfileThumbnailCollectionViewCell", bundle: nil)
 self.collectionView.register(nib, forCellWithReuseIdentifier: "ProfileThumbnailCollectionViewCell")

【讨论】:

嗨,通过 NIB 或通过代码创建 UICollectionViewCell 没有区别。错误仍然出现。【参考方案2】:

在调查了几个小时后,我无意中找到了一个变通的解决方案,但这肯定不应该被视为最终解决方案。

class ProfileThumbnailCollectionViewCell: UICollectionViewCell 
    override init(frame: CGRect) 
        super.init(frame: frame)
        
        // Content View
        contentView.backgroundColor = .yellow

        // ******** This is the weird point ********
        let targetCellWidth = UIScreen.main.bounds.width - 1


        NSLayoutConstraint.activate([
            contentView.widthAnchor.constraint(equalToConstant: targetCellWidth)
        ])
    

    

UICollectionView 不会引发任何错误,并正确显示标题视图和UICollectionView

如果您对这种情况有任何想法,请在此处留下您的评论。非常感谢您的信息!谢谢!

【讨论】:

以上是关于当项目大小是自动的并且显示标题时,UICollectionView 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何使用viewForSupplementaryElementOfKind - swift显示UICollectionView的特定单元格

集合视图自动布局

在布局之间制作动画时,自动布局不会调整 Uicollectionviewcell 项目的大小

当显示窗格的实时调整大小时,它在拖动时很困难,并且窗格顶部在释放后被隐藏

更改尺寸等级时会影响约束

高度自动 - 调整浏览器大小时显示隐藏内容