The answer of Pablo Romeu above ( https://stackoverflow.com/a/12735/ ... ) really helped me in my problem. I had to do a few things differently, however, so that this worked for my problem. First, I did not need to call layoutIfNeeded() so often. I only needed to call it on collectionView in the systemLayoutSizeFitting function.
Secondly, I had automatic layout restrictions for my collection view in a table view cell to give it some addition. So I had to subtract targetSize.width and targetSize.width fields from targetSize.width when setting the width of collectionView.frame . I also had to add top and bottom margins to the return value of the CGSize height.
To get these constraint constants, I had the opportunity to either create outputs for the constraints, either hard code their constants, or find them by identifier. I decided to go with the third option to make my table view cell class easy to repeat. In the end, that was all I needed for it to work:
class CollectionTableViewCell: UITableViewCell { // MARK: - // MARK: Properties @IBOutlet weak var collectionView: UICollectionView! { didSet { collectionViewLayout?.estimatedItemSize = CGSize(width: 1, height: 1) selectionStyle = .none } } var collectionViewLayout: UICollectionViewFlowLayout? { return collectionView.collectionViewLayout as? UICollectionViewFlowLayout } // MARK: - // MARK: UIView functions override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { collectionView.layoutIfNeeded() let topConstraintConstant = contentView.constraint(byIdentifier: "topAnchor")?.constant ?? 0 let bottomConstraintConstant = contentView.constraint(byIdentifier: "bottomAnchor")?.constant ?? 0 let trailingConstraintConstant = contentView.constraint(byIdentifier: "trailingAnchor")?.constant ?? 0 let leadingConstraintConstant = contentView.constraint(byIdentifier: "leadingAnchor")?.constant ?? 0 collectionView.frame = CGRect(x: 0, y: 0, width: targetSize.width - trailingConstraintConstant - leadingConstraintConstant, height: 1) let size = collectionView.collectionViewLayout.collectionViewContentSize let newSize = CGSize(width: size.width, height: size.height + topConstraintConstant + bottomConstraintConstant) return newSize } }
As a helper function to get the identifier constraint, I add the following extension:
extension UIView { func constraint(byIdentifier identifier: String) -> NSLayoutConstraint? { return constraints.first(where: { $0.identifier == identifier }) } }
NOTE. You will need to set an identifier for these restrictions in your storyboard or where they are created. If they do not have constant 0, then it does not matter. Just like in Pablo's answer, you will need to use UICollectionViewFlowLayout as a layout to represent your collection. Finally, be sure to associate collectionView IBOutlet with your storyboard.
With the custom table view cell above, I can now subclass it in any other table view cell that needs the collection view and force it to implement the UICollectionViewDelegateFlowLayout and UICollectionViewDataSource . Hope this is helpful to someone else!