ios - 在 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 上使用上面的代码:
和 12.2:
解决方案
您的代码中有一些问题:
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)
}
希望这对您来说很清楚并且对您有所帮助!
推荐阅读
- image-processing - 高斯线性的拉普拉斯算子
- firebase - Firebase FCM 令牌和给每个用户的自定义消息
- r - 使用 gsub 删除第一个数字字符之前的所有字符串
- html - 使用获取 URL 中的值填写 Web 表单;字段名称问题
- javascript - 如果 ID 是数字,则 Bootstrap 手风琴不起作用
- html - 复选框处于“选中”状态时无法更改 CSS 样式
- wordpress - 根据另一个选择字段过滤 ACF 选择字段
- hibernate - 在 JPA/Hibernate 上映射日期时,日期和日历之间有区别吗?
- operating-system - 敏感指令和特权指令的区别
- javascript - 如何使用 Selenium 提取对当前网站有影响的可能操作?