如何在 UIView 中包含溢出的 UILabel 文本?

How to contain overflowed UILabel's text inside UIView?

import UIKit

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemBackground

    setup()
  }

  private func setup() {
    let container = UIView()
    container.backgroundColor = .red
    view.addSubview(container)
    
    container.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      container.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
      container.heightAnchor.constraint(equalToConstant: 100.0),
      container.widthAnchor.constraint(equalToConstant: 100.0)
    ])
    
    let label = UILabel()
    label.text = "This is my text.This is my text.This is my text.This is my text.This is my text.This is my text.This is my text."
    label.numberOfLines = 0
    label.sizeToFit()
    
    container.addSubview(label)
    
    label.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      label.widthAnchor.constraint(equalTo: container.widthAnchor)
    ])
  }
}

我有一个 100x100 UIView,我想在其中添加一个 UILabelUILabel 溢出 UIView。我想知道如何在 UIView 中包含 UILabel 而不会溢出?

我试过了,

label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
  label.topAnchor.constraint(equalTo: container.topAnchor),
  label.bottomAnchor.constraint(equalTo: container.bottomAnchor),
  label.widthAnchor.constraint(equalTo: container.widthAnchor)
])

但出于某种原因,这增加了垂直填充,

并且激活这些约束还有另一个问题,那就是当文本长度很小时,文本会垂直居中。我想一直把它固定在左上角。

以下是我对正在发生的事情的看法。

我将从填充位开始。我相信这两种情况实际上是相同的:

默认行为是将文本居中,在这两种情况下都会发生这种情况。在你说有填充的第二种情况(右图)中,UILabel 无法容纳该字体大小的另一行文本,因此它停在第 4 行并将文本与标签的框架居中。

例如,如果将标签高度增加到 500,您会发现它不是随机填充,而是居中。

这是给定高度可能支持的文本行数的简单近似值。

let label = UILabel()
label.text = "This is my text.This is my text.This is my text.This is my text.This is my text.This is my text.This is my text."
label.numberOfLines = 0
label.sizeToFit()

// I added these line
print("Max lines that will fit: \(floor(100.0 / label.font.lineHeight))")
print("Max lines that will fit: \(floor(130.0 / label.font.lineHeight))")

打印出来的答案是4.0和6.0。当我如下将标签高度增加到 130 时,它给了我大约 6 行:

// Inside your set up function
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    container.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    container.heightAnchor.constraint(equalToConstant: 130.0),
    container.widthAnchor.constraint(equalToConstant: 100.0)
])

let label = UILabel()
label.text = "This is my text.This is my text.This is my text.This is my text.This is my text.This is my text.This is my text."
label.numberOfLines = 0
label.sizeToFit()

所以我希望这能解释有关填充的部分。

现在来谈谈如何防止溢出,我认为您的自动版式已经可以防止溢出。我认为在容器上也添加 clipsToBounds 将防止标签溢出。

最后,将标签与顶部对齐是一个完全不同的问题。然而,这里有一个 wonderful solution I came across 是 UILabel 的子类并覆盖 drawRect ,如下所示:

// Credit to Ma11hew28 
class TopAlignedLabel: UILabel {
  override func drawText(in rect: CGRect) {
    let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
    super.drawText(in: textRect)
  }
}

// Your new set up function will be like this, I have commented
// the 2 changes I made
private func setup() {
    let container = UIView()
    container.backgroundColor = .red
    view.addSubview(container)
    
    container.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        container.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        container.heightAnchor.constraint(equalToConstant: 100.0),
        container.widthAnchor.constraint(equalToConstant: 100.0)
    ])
    
    // Use top aligned label
    let label = TopAlignedLabel()
    label.text = "This is my text."
    label.numberOfLines = 0
    label.sizeToFit()
    
    container.addSubview(label)
    
    label.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        label.widthAnchor.constraint(equalTo: container.widthAnchor)
    ])
    
    // Clip container to bounds to prevent overflow
    container.clipsToBounds = true
}

最终版本防止溢出并在文本较少时将文本对齐到顶部: