I'm trying to make a news feed like app using UICollectionView with adaptive cell height. I found this tutorial and used the option "2. Solution for iOS 11+" for my code, which works perfectly fine. However, whenever I try to add more subviews to the cell and layout them as required, I get this error: "Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want."
Below is my code which gives me an error.
NewsFeedViewController:
import UIKit
class NewsFeedViewController: UIViewController {
var friends: [Friend] = []
var allPosts: [Post?] = []
var shuffledPosts: [Post?] = []
var collectionView: UICollectionView = {
let layout = NewsFeedFlowLayout()
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize // new
layout.scrollDirection = .vertical
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = UIColor.systemRed
collection.isScrollEnabled = true
collection.contentInsetAdjustmentBehavior = .always
collection.translatesAutoresizingMaskIntoConstraints = false
return collection
}()
let cellID = "NewsFeedCollectionViewCell"
override func viewDidLoad() {
super.viewDidLoad()
getPosts()
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(NewsFeedCollectionViewCell.self, forCellWithReuseIdentifier: cellID)
collectionView.pin(to: view)
}
func getPosts() {
friends = FriendFactory().friends
allPosts = friends.flatMap{$0.posts}
var tempPosts = allPosts
for _ in allPosts {
if let index = tempPosts.indices.randomElement() {
let post = tempPosts[index]
shuffledPosts.append(post)
tempPosts.remove(at: index)
}
}
}
}
extension NewsFeedViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return shuffledPosts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! NewsFeedCollectionViewCell
let post = shuffledPosts[indexPath.row]
let friend = friends.first(where: {$0.identifier == post?.authorID})
let text = post?.postText
cell.configurePostText(postText: text!)
return cell
}
}
NewsFeedCollectionViewCell:
import UIKit
class NewsFeedCollectionViewCell: UICollectionViewCell {
var selectedPost = Post()
var likeBarView: LikeBarView = {
let view = LikeBarView()
view.backgroundColor = .systemIndigo
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var postTextLabel: UILabel = {
let label = UILabel()
label.font = label.font.withSize(20)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .systemYellow
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addViews() {
addSubview(likeBarView)
addSubview(postTextLabel)
}
func configurePostText(postText: String) {
postTextLabel.text = postText
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
layoutIfNeeded()
layoutAttributes.frame.size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return layoutAttributes
}
func setupConstraints() {
NSLayoutConstraint.activate([
likeBarView.topAnchor.constraint(equalTo: topAnchor, constant: 20),
likeBarView.leadingAnchor.constraint(equalTo: leadingAnchor),
likeBarView.trailingAnchor.constraint(equalTo: trailingAnchor),
likeBarView.heightAnchor.constraint(equalToConstant: 100),
postTextLabel.topAnchor.constraint(equalTo: likeBarView.bottomAnchor, constant: 20),
postTextLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
postTextLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
postTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
])
}
}
NewsFeedFlowLayout:
import UIKit
final class NewsFeedFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributesObjects = super.layoutAttributesForElements(in: rect)?.map{ $0.copy() } as? [UICollectionViewLayoutAttributes]
layoutAttributesObjects?.forEach({ layoutAttributes in
if layoutAttributes.representedElementCategory == .cell {
if let newFrame = layoutAttributesForItem(at: layoutAttributes.indexPath)?.frame {
layoutAttributes.frame = newFrame
}
}
})
return layoutAttributesObjects
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let collectionView = collectionView else {
fatalError()
}
guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
return nil
}
layoutAttributes.frame.origin.x = sectionInset.left
layoutAttributes.frame.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right
return layoutAttributes
}
}
I enclose the screenshot of the collection view I'm getting and the screenshot of the analysis of the error done here, which is saying that the height of the cell is not dynamic if I'm getting it right.
What am I doing wrong?
Adding this line of code to NewsFeedViewController silenced the error: