首页 > 解决方案 > 在 iOS 13 之前的设备上为单元格返回了意外的高度

问题描述

我正在制作一个消息传递应用程序并根据单元格标签之一的估计框架确定消息单元格的高度。出于某种原因,在运行 iOS 13 之前的操作系统的小型设备上,返回的高度比需要的大得多。

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let message = messageArr[indexPath.item].messageBody 
        let size = CGSize(width: collectionView.frame.width - 120, height: 1000)            
        let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
        var estimatedFrame = CGRect()
        var cellLbl: UILabel!
        var personLbl: UILabel!

        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CurrentUserMessageCell", for: indexPath) as? CurrentUserMessageCell {
            cellLbl = cell.messageLbl
            personLbl = cell.personLbl
            cell.translatesAutoresizingMaskIntoConstraints = false
        }

        if let font = cellLbl.font {
            estimatedFrame = NSString(string: message).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font: font], context: nil)
        }

        let height = estimatedFrame.height + personLbl.frame.height + 16
        return CGSize(width: collectionView.frame.width, height: height)
}

在我更改标签的约束之前,这在所有设备上都非常有效,因此它并不总是占据大部分可用宽度,即使是短消息也是如此。这就是collectionView.frame.width - 120从哪里来的(宽度 - 插图,前导/尾随空间)。

我已将问题缩小到标签的估计宽度(在size属性中确定) - 但为什么不准确的值不会像 12.2 那样影响 iOS 13.1?我怎样才能更准确地确定这个值?我尝试通过标签的插入和前导/尾随空间来抵消宽度,但是单元格高度总是太短(在 iOS 13.1 之前字符被截断,而 13.1 只是失去了一点填充)。

这是在 iOS 13.1 上使用上面的代码:

IOS 13

和 12.2:

在此处输入图像描述

标签: iosswiftuicollectionviewcell

解决方案


您的代码中有一些问题:

  • dequeueReusableCell在 .之外调用是不正确的cellForItemAt
  • 内部sizeForItemAt单元格仍未创建,因此请勿尝试访问它。

...但是为什么不准确的值不会像 12.2 那样影响 iOS 13.1?

有很多因素,也许只是随机的。可能是因为您在内部创建单元格,sizeForItem或者您的估计大小计算逻辑在某些情况下不正确。尝试从下面dequeueReusableCell删除并使用函数进行计算。


您可以将此扩展添加到您的单元格String中计算宽度高度附加信息

extension String {

    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)

        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)

        return ceil(boundingBox.width)
    }
}

内部sizeForItemAt (使用示例)

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let message = messageArr[indexPath.item].messageBody

    // if you want the cell to take the entire width of the collectionView
    // then you can use this width as constrained width
    let estimatedCellWidth = collectionView.frame.width

    // If you want your label inside `cell` have a margin from each side
    // you can add some value to `estimatedHeight` and make it bigger
    // like: estimatedHeight + *some value*, to make final height of cell bigger
    let estimatedHeight = message.height(withConstrainedWidth: estimatedCellWidth, font: /*your current font*/)

    // Some additional logic of calculation if needed ...

    return CGSize(width: estimatedCellWidth, height: estimatedHeight)
}

希望这对您来说很清楚并且对您有所帮助!


推荐阅读