Using swift-5.0.1, iOS-12.2,
I observe a strange behaviour on a UICollectionView:
There are screen-wide cells that show an image inside my collectionView.
My observation is that whenever the image could not be fetched - in such a case, the above cell turns out to be bigger in size than expected. The question is why ??
The following image illustrates the problem:
Any image that cannot be fetched can obviously not be displayed and its cell gets the backgroundColer equal to red. This all works. But why is the one cell that is right above the nil-cell increased in size ???
All cells should be the size indicated by the yellow boxes.
I have two places inside my code where the height of a cell is touched:
- inside the UICollectionViewCell initialiser :
mainImageView.heightAnchor.constraint(equalToConstant: 150)
- inside the UICollectionViewController
sizeForItemAt
:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width, height: 150)
}
Both are set to 150 ! But in the error-case, the big cell is bigger than 150 - why ??
Here is the code in more detail:
import UIKit
class PhotoListCollectionViewCell: UICollectionViewCell {
var mainImageView: UIImageView = {
let imgV = UIImageView()
imgV.contentMode = .scaleAspectFill
return imgV
}()
var dateLabel = UILabel()
var descriptionLabel = UILabel()
var nameLabel = UILabel()
var descContainerHeightConstraint = NSLayoutConstraint()
var photoListCellViewModel : PhotoListCellViewModel? {
didSet {
nameLabel.text = photoListCellViewModel?.titleText
descriptionLabel.text = photoListCellViewModel?.descText
mainImageView.sd_setImage(with: URL( string: photoListCellViewModel?.imageUrl ?? "" ), completed: nil)
dateLabel.text = photoListCellViewModel?.dateText
}
}
func setConstraints() {
addSubview(mainImageView)
mainImageView.translatesAutoresizingMaskIntoConstraints = false
mainImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
mainImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
mainImageView.heightAnchor.constraint(equalToConstant: 150)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .red
self.setConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class CollectionViewController: BaseListController, UICollectionViewDelegateFlowLayout {
fileprivate let cellId = "cellId"
lazy var viewModel: PhotoListViewModel = {
return PhotoListViewModel()
}()
override func viewDidLoad() {
super.viewDidLoad()
initView()
initVM()
}
func initView() {
self.navigationItem.title = "NavBar"
collectionView.register(PhotoListCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView.backgroundColor = .white
}
func initVM() { ... }
func showAlert( _ message: String ) { ... }
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel.numberOfCells
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? PhotoListCollectionViewCell else {
fatalError("Cell could not be dequeued")
}
let cellVM = viewModel.getCellViewModel( at: indexPath )
cell.photoListCellViewModel = cellVM
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width, height: 150)
}
}
The below code is most likely irrelevant for the question (but for completeness reasons...)
class BaseListController: UICollectionViewController {
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also tried to change imgV.contentMode = .scaleAspectFill
to imgV.contentMode = .scaleAspectFit
. But then the I loose the full width of the images as seen in the illustration here:
But I think I am getting closer. What could be the ideal setting ?