UICollectionViewCell systemLayoutSizeFittingSize r

2020-06-03 04:50发布

问题:

I've been toying with dynamic UICollectionViewCell's and have noticed that on iOS 8 calling cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) on a UICollectionViewCell returns an incorrect width. Currently the only workaround that addresses this is to explicitly add a width constraint to force the cell's width. The below code is used in a Cell subclass:

func calculatePreferredHeightForFrame(frame: CGRect) -> CGFloat {
    var newFrame = frame
    self.frame = newFrame
    let widthConstraint = NSLayoutConstraint(item: contentView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: CGRectGetWidth(frame))
    contentView.addConstraint(widthConstraint)
    self.setNeedsUpdateConstraints()
    self.updateConstraintsIfNeeded()
    self.setNeedsLayout()
    self.layoutIfNeeded()
    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = CGFloat(ceilf(Float(desiredHeight)))
    contentView.removeConstraint(widthConstraint)
    return CGRectGetHeight(newFrame)
}

I know that with iOS 8 and dynamic UICollectionViewFlowLayout that the UICollectionViewCell's contentView handles constraints differently but is there something I'm missing here? What does one need to do to ensure that systemLayoutSizeFittingSize uses a particular width on a cell?

I also came across this post (Specifying one Dimension of Cells in UICollectionView using Auto Layout) and believe this might actually invalidate my question. Perhaps UICollectionViewFlowLayout is not designed for cells with only one dynamic dimension, but that still doesn't explain why the cell gives an unusual width.

回答1:

I've faced same issue. The key is using priorities when getting systemLayoutSize.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (!self.prototypeCell) {
        self.prototypeCell = [TrainingMenuCell new];
        self.prototypeCell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
    }

    [self configureCell:self.prototypeCell forIndexPath:indexPath];
    [self.prototypeCell setNeedsLayout];
    [self.prototypeCell layoutIfNeeded];

    CGFloat width = CGRectGetWidth(collectionView.frame) - 12;
    CGSize fittingSize = UILayoutFittingCompressedSize;
    fittingSize.width = width;

    CGSize size = [self.prototypeCell.contentView systemLayoutSizeFittingSize:fittingSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityDefaultLow];
    return size;
}


回答2:

A SSCCE (example) is available at my repository:

https://github.com/knutvalen/sandbox-uicollectionview

Below is a snippet of the main code.

Swift 4

var array = ["foo", "bar", "baz"]

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)

    if let myCell = cell as? MyCollectionViewCell {
        myCell.titleLabel.text = array[indexPath.item]
        myCell.subtitleLabel.text = "my subtitle for " + array[indexPath.item] + " is a very long text with many letters and words"
        return myCell
    }

    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let forcedWidth: CGFloat = 200
    let myCell = MyCollectionViewCell()
    myCell.titleLabel.text = array[indexPath.item]
    myCell.subtitleLabel.text = "my subtitle for " + array[indexPath.item] + " is a very long text with many letters and words"
    myCell.subtitleLabel.preferredMaxLayoutWidth = forcedWidth - (myCell.myMargin * 2)
    let optimalSize = myCell.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
    let forcedSize = CGSize(width: forcedWidth, height: optimalSize.height)
    return forcedSize
}

Output



回答3:

In my case, it was not calculating width correctly because I was missing those two lines before calling systemLayoutSizeFittingSize method:

[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];

Spent hours to discover it so hope it could be helpful for others having similar issue.