【问题标题】:UILabel with custom UIEdgeInsets truncating in UITableViewCell在 UITableViewCell 中截断自定义 UIEdgeInsets 的 UILabel
【发布时间】:2017-06-04 19:38:26
【问题描述】:

我有一个包含 UILabel 的单元格的 UITableView。 UILabel 有一个自定义的 UIEdgeInset。我将 UILabel 子类化并像这样设置 UIEdgeInsets:

override func drawText(in rect: CGRect) {
    super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}

override var intrinsicContentSize: CGSize {
    var contentSize = super.intrinsicContentSize
    contentSize.width += leftInset + rightInset
    contentSize.height += topInset + bottomInset
    return contentSize
}

但是当我在 UILabel 中有更多行时,标签有时会被截断。 我已经将行高配置为 UITableViewAutomaticDimension 并设置了 estimatedRowHeight。约束也很好。 问题似乎出在我设置 UIEdgeInsets 时,因为如果我不自定义它,它就可以正常工作。

可能我应该告诉单元格在设置插图后更新约束,但到目前为止我还不能这样做。

故事板中添加的约束。 BottomTopLeadingTrailing 与超级视图(UITableViewCell)相关。所有常量设置为 0。

cellForRowAtIndexPath中的代码如下:

let cell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell", for: indexPath) as! AnswerCell  
cell.answerLabel.text = alternatives[indexPath.row]
return cell

【问题讨论】:

  • 卢卡斯你是说标签只有在标签中有不止一行时才会被截断?通过截断我假设您的意思是被单元格分隔器水平切断 - 即单元格没有正确扩展以容纳标签中的额外行。对吗?
  • 你的标签子类对我来说是正确的,因为它具有自我调整大小的表格视图单元格,所以你可能想要分享你用来设置表格视图单元格和子视图的代码。
  • @thecloud_of_unknowing 是的,字符串不适合视图。当我有不止一条线时,它不会一直发生。似乎没有考虑我设置的新 UIEdgeInsets 。所以在某些情况下它很合适,但在其他情况下它可能会被截断,因为它希望有更多的宽度空间。
  • @jamesk 除此之外没有太多代码。在viewDidLoad 中,我将rowHeight 设置为tableView.rowHeight = UITableViewAutomaticDimension,同时将estimatedRowHeight 设置为tableView.estimatedRowHeight = 120。而在cellForRowAtIndexPath我只设置了标签文字。
  • 刚刚添加了更多信息,但看起来很简单,与问题无关。

标签: ios swift uitableview autolayout uilabel


【解决方案1】:

在多行标签的自动布局期间,UILabel 不会使用其intrinsicContentSize 属性提供的宽度来计算其高度,而是使用preferredMaxLayoutWidth,它尚未根据您的内容插入进行调整。

UILabel 的头文件为preferredMaxLayoutWidth 提供以下注释:

// 如果非零,则在确定多行标签的-intrinsicContentSize 时使用

在您的情况下,preferredMaxLayoutWidth 的所需值是标签的自动布局宽度(即表格视图单元格的宽度)减去任何左右内容插图。设置该属性最实用的方法是在表格视图单元格子类中,因为这是在自动布局期间访问表格视图单元格宽度最直接的地方。

当为自调整大小的表格视图单元格执行自动布局时,UITableView 在每个单元格上调用systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority)。 (参见WWDC 2014 Session 226。)传递给该方法的拟合大小是高度为零的表格视图的宽度,水平拟合优先级为UILayoutPriorityRequired

因此,覆盖该方法并为preferredMaxLayoutWidth 设置所需的值:

//  AnswerCell.swift

@IBOutlet weak var answerLabel: AnswerLabel!

override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
    answerLabel.preferredMaxLayoutWidth = targetSize.width - answerLabel.insets.left - answerLabel.insets.right
    return super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
}

效果是,在自动布局期间计算其高度时,UILabel 使用已针对您的内容插入进行调整的值preferredMaxLayoutWidth,从而确保 UITableView 接收到其单元格的正确高度。

附录

或者,您可以简单地更改标签上的约束以插入整个标签,或者直接在表格视图单元格的内容视图中或通过将其嵌入到内容视图的另一个子视图中,而不是上述所有内容。

【讨论】:

  • @ChristianSchnorr 绘制文本时不使用intrinsicContentSize 的值。通过插入传递给drawText(in:) 的矩形来保留水平空间。尽管intrinsicContentSize 不是造成 OP 问题的原因,但它仍然是不正确的——它返回的宽度大于标签的超级视图的宽度。请保持文明。
  • intrinsicContentSize 确定视图的对齐大小,即除非alignmentRectInsets 不为零,否则视图的边界大小。当视图在绘制之前插入其边界时,intrinsicContentSize 需要考虑这一点。
  • @ChristianSchnorr 你是对的。我在设置 preferredMaxLayoutWidth 之前写了部分答案,这意味着(当时)super.intrinsicContentSize 返回的宽度似乎可以解释内容插入(即父单元格的宽度)。但是一旦设置了preferredMaxLayoutWidth 以便它考虑内容插入,super.intrinsicContentSize 就会返回一个不包括插入的宽度,并且如您所说,需要调整。我已经进行了相应的编辑。
【解决方案2】:

从情节提要的属性检查器部分设置 UILable 的这两个属性

  1. 到 0 的行
  2. 换行换行

或者您也可以通过代码设置这些属性。

self.answerLabel.numberOfLines = 0
self.answerLabel.lineBreakMode = .byWordWrapping

并将下面的代码放在你的 UILable 的子类中

class CustomLabel: UILabel {

@IBInspectable var topInset: CGFloat = 5.0
@IBInspectable var bottomInset: CGFloat = 5.0
@IBInspectable var leftInset: CGFloat = 20.0
@IBInspectable var rightInset: CGFloat = 5.0

override func drawText(in rect: CGRect) {
    let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
    super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}

override var intrinsicContentSize: CGSize {
    get {
        var contentSize = super.intrinsicContentSize
        contentSize.height += topInset + bottomInset
        contentSize.width += leftInset + rightInset
        return contentSize
    }
}

}

试试这个。

希望它对你有用..

【讨论】:

  • 为了使intrinsicContentSize 在标签上有用,您需要告诉他们您希望他们在哪里中断:preferredMaxLayoutWidth
【解决方案3】:

我这样子分类了我的标签:

class PaddingLabel: UILabel {

    var topInset: CGFloat = 0
    var bottomInset: CGFloat = 0
    var leftInset: CGFloat = 0
    var rightInset: CGFloat = 0

    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override var intrinsicContentSize: CGSize {
        get {
            var contentSize = super.intrinsicContentSize
            contentSize.height += topInset + bottomInset
            contentSize.width += leftInset + rightInset
            return contentSize
        }
    }
}

也许是 get 不见了。

【讨论】:

  • 其实不是这样的。当您有一个只读计算属性时,您可以通过删除 get 关键字及其大括号来简化它。
  • 嗯,这个标签对我来说很好用。我需要做的就是设置变量(上、下、左、右插图),一切顺利
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多