【问题标题】:How to size a UIScrollView to fit an unknown amount of text in a UILabel?如何调整 UIScrollView 的大小以适应 UILabel 中未知数量的文本?
【发布时间】:2026-01-08 04:25:01
【问题描述】:

我在其中一个视图中添加了一个滚动视图子视图,但无法使其高度准确地适合滚动视图显示的内容,即 UILabel 中的文本。高度需要是动态的(即文本长度的一个因素),因为我正在为许多不同的文本长度实例化这个视图。每当我登录 label.frame.bounds 时,我都会返回 (0,0)。我也在几个地方尝试过sizeToFits(),但运气不佳。

我的目标是让滚动视图在到达最后一行文本时结束。另外,我只使用编程约束。

我的代码的精简版如下:

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {
    let scrollView = UIScrollView()
    let containerView = UIView()
    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.delegate = self

        // This needs to change
        scrollView.contentSize = CGSize(width: 375, height: 1000)

        scrollView.addSubview(containerView)
        view.addSubview(scrollView)

        label.text = unknownAmountOfText()
        label.backgroundColor = .gray


        containerView.isUserInteractionEnabled = true
        containerView.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false

        label.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true

        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        scrollView.frame = view.bounds
        containerView.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
    }
}

感谢任何帮助。

找到解决方案:

func heightForLabel(text: String, font: UIFont, lineHeight: CGFloat, width: CGFloat) -> CGFloat {
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
    label.numberOfLines = 0
    label.lineBreakMode = NSLineBreakMode.byWordWrapping
    label.font = font
    label.text = text
    label.setLineHeight(lineHeight: lineHeight)
    label.sizeToFit()
    return label.frame.height
}

我在网上找到了这个解决方案,它为我提供了根据标签高度为 scrollView 高度设置适当内容大小所需的内容。理想情况下,如果没有这个功能,我可以确定这一点,但现在我很满意。

【问题讨论】:

  • 您为什么要以编程方式而不是在界面生成器中执行此操作?处理这个问题的最简单方法是使用布局约束,尽管这在代码中会很快变得丑陋。
  • 为什么要把 UILabel 放到 UIScrollView 中,然后尝试调整滚动视图的大小以适应标签?你想滚动吗?如果是这样,为什么要调整滚动视图的大小?如果您不想滚动,为什么要将标签放在滚动视图中?
  • 我想尝试一个只有程序化布局的项目。我想在我的下一个项目中,我将使用界面生成器。但就目前而言,我对这个项目的切换太深了,所以我确实需要以编程方式解决这个问题。
  • 我确实想要滚动。文本可能比屏幕上可以容纳的大,所以在这种情况下,我希望用户能够滚动浏览文本。我需要在某个时候弄清楚文本的大小才能确定滚动视图的大小。
  • 好吧,还是有点混乱……你的目标是拥有一个“框架”的 ScrollView 吗?因此,如果文本很短,并且可能只需要屏幕高度的 1/4,那么您希望在标签周围有一个屏幕高度 1/4 的可见框架?如果文本有点多,所以它是 3/4 高度,你希望可见框架占据屏幕的 3/4 吗?如果文本是屏幕的两倍高,那么可见框架是全屏,文本滚动?

标签: ios swift uiscrollview uikit uiscrollviewdelegate


【解决方案1】:

使用容器视图为标签提供顶部、左侧、右侧和底部约束。 和 设置 label.numberOfLines = 0 还要确保您已为 containerView 提供了顶部、左侧、右侧和底部约束。这将解决您的问题

【讨论】:

    【解决方案2】:

    UIScrollView 及其内容大小的关键是设置约束,以便实际的 content 定义 contentSize。

    举个简单的例子:假设你有一个宽度为 200 高度为 200 的 UIScrollView。现在你在其中放置一个宽度为 100 高度为 400 的 UIView。视图应该上下滚动,但不能左右。您可以将视图限制为 100x400,然后将顶部、底部、左侧和右侧“固定”到滚动视图的两侧,AutoLayout 将“自动”设置滚动视图的 contentSize。

    当您添加可以改变大小的子视图时——无论是显式(代码、用户交互)还是隐式——如果约束设置正确,这些更改也将“自动-神奇地”调整滚动视图的 contentSize。

    所以...这是您尝试做的一个示例:

    import UIKit
    
    class ViewController: UIViewController, UIScrollViewDelegate {
    
        let scrollView = UIScrollView()
        let label = UILabel()
    
        let s1 = "1. This is the first line of text in the label. It has words and punctuation, but no embedded line-breaks, so what you see here is normal UILabel word-wrapping."
        var counter = 1
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // turn off translatesAutoresizingMaskIntoConstraints, because we're going to set them
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            label.translatesAutoresizingMaskIntoConstraints = false
    
            // set background colors, just so we can see the bounding boxes
            self.view.backgroundColor = UIColor(red: 1.0, green: 0.7, blue: 0.3, alpha: 1.0)
            scrollView.backgroundColor = UIColor(red: 0.8, green: 0.8, blue: 1.0, alpha: 1.0)
            label.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
    
            // add the label to the scrollView, and the scrollView to the "main" view
            scrollView.addSubview(label)
            self.view.addSubview(scrollView)
    
            // set top, left, right constraints on scrollView to
            // "main" view + 8.0 padding on each side
            scrollView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 8.0).isActive = true
            scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8.0).isActive = true
            scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -8.0).isActive = true
    
            // set the height constraint on the scrollView to 0.5 * the main view height
            scrollView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true
    
            // set top, left, right AND bottom constraints on label to
            // scrollView + 8.0 padding on each side
            label.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 8.0).isActive = true
            label.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 8.0).isActive = true
            label.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -8.0).isActive = true
            label.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -8.0).isActive = true
    
            // set the width of the label to the width of the scrollView (-16 for 8.0 padding on each side)
            label.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -16.0).isActive = true
    
            // configure label: Zero lines + Word Wrapping
            label.numberOfLines = 0
            label.lineBreakMode = NSLineBreakMode.byWordWrapping
            label.font = UIFont.systemFont(ofSize: 17.0)
    
            // set the text of the label
            label.text = s1
    
            // ok, we're done... but let's add a button to change the label text, so we
            // can "see the magic" happening
            let b = UIButton(type: UIButtonType.system)
            b.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(b)
            b.setTitle("Add a Line", for: .normal)
            b.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 24.0).isActive = true
            b.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
            b.addTarget(self, action: #selector(self.btnTap(_:)), for: .touchUpInside)
    
        }
    
        func btnTap(_ sender: Any) {
            if let t = label.text {
                counter += 1
                label.text = t + "\n\n\(counter). Another line"
            }
        }
    
    }
    

    【讨论】:

    • 感谢您让我充分了解了滚动视图的正确使用和魔力。这个例子很完美
    • 还有一个问题...是否有必要为标签设置widthAnchor,因为您已经设置了它的前导和尾随锚点?
    • 是的......前导、尾随、顶部和底部锚告诉滚动视图“这些是你的 contentSize 的边缘”......宽度锚告诉标签“不要让自己变得更宽比这“...没有高度锚,因为您希望标签拉伸到适合文本所需的高度。
    【解决方案3】:

    从界面生成器设置自动布局约束,如图所示。 enter image description here

    我将 UIScrollView 的高度设置为 UIView 的 0.2

    然后将 UIlabel 从 MainStoryBoard 拖到视图控制器。

    在 viewdidload 方法中添加这两行。

    拖动标签.numberOfLines = 0

    draggedlabel.lineBreakMode = NSLineBreakMode.byWordWrapping

    【讨论】: