I encounter a weird error that when I want to set the estimatedItemSize
property of the UICollectionView
to UICollectionViewFlowLayout.automaticSize
and show a section header at the same time.
With the below code, the app can show the UICollectionView
properly. However, once the user scrolls on the UICollectionView
, the app crashes with a recursive call to the function updateVisibleCellsNow
.
I find a workaround solution from another StackOverflow question by setting the estimatedItemSize
from automaticSize
to none
. However, I want to keep the auto-layout features set at the UICollectionViewCell
instead of calculating the cell height myself. Is there any better solution? Thank you.
Here is my code about the 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()
}
}
}
Here is my code about the 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")
}
}
2
Answers
After investigating for a few hours, I accidentally found a workaround solution but surely this should not be considered as the final solution.
The
UICollectionView
does not throw any error and displays both the header view and theUICollectionView
properly.If you can find any idea about this situation, please leave your comment here. Your information is highly appreciated! Thank you!
call registerNib in ViewDidLoad