UIScrollView 随着内容的固有大小扩展到高度 X,然后滚动

UIScrollView that expands with contents' instrinsic size until height X, and then scrolls

我基本上是在尝试重现警报的标题和消息部分的行为。

标题和消息标签似乎在滚动视图中。如果标签文本增加,则警报高度也会随着标签的固有内容大小而增加。但在一定高度时,警报高度停止增加,标题和消息文本变为可滚动。

我阅读的内容:

文章

堆栈溢出

答案可能就在那里,但我无法抽象出来。

我试过的:

我只关注带有两个标签的滚动视图,我试图做一个最小的例子,其中 parent 视图会根据滚动视图的固有高度调整大小。我玩过很多限制。这是一个不起作用的组合(在许多组合中):

我曾与 auto layout and normal constraints and even intrinsic content sizes. Also, I do know how to get a basic scroll view working with auto layout 合作过。然而,我从来没有做过任何有优先级和内容拥抱和压缩阻力的事情。从我所做的阅读来看,我对它们的含义有肤浅的理解,但我不知道如何在这种情况下应用它们。我的猜测是我需要在内容拥抱和优先级方面做一些事情。

你的问题光靠约束是解决不了的,你得用一些代码。那是因为滚动视图没有固有的内容大小。

所以,创建一个滚动视图的子类。也许给它一个 属性 或一个委托或其他东西来告诉它它的最大高度应该是多少。

在子类中,每当内容大小发生变化时,都使固有内容大小无效,并将新的固有大小计算为内容大小的最小值和允许的最大大小。

一旦滚动视图具有固有大小,您在它和它的超级视图之间的约束实际上会对您的问题做一些有意义的事情。

您应该能够通过纯自动版式实现此解决方案。

通常,如果我希望标签随着内容的垂直增长而增长,我会这样做

[label setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; 
[label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

为了让您的滚动视图符合您的要求,您需要确保可以绘制一条线连接滚动视图的顶部,一直通过标签连接到滚动视图的底部,以便它可以计算它的高度.为了将 scrollview 限制在它的父视图中,你可以设置一个高度约束,其中父视图的乘数为 0.8

我想我已经用纯自动布局实现了你想要的效果。

结构

首先,让我向您展示我的结构:

Content View是白色背景的view,Caller View和Bottom View有固定的高度。 Bottom View 有你的按钮,Caller View 有你的标题。

解决方案

因此,在设置基本约束后(注意滚动视图内的视图具有滚动视图的顶部、左侧、右侧和底部并且宽度相等)问题是滚动视图不知道尺寸是多少应该有。 所以这就是我所做的:

我希望卷轴可以增长到最大值。所以我在设置最大值的超级视图中添加了一个比例高度:

然而,这带来了两个问题:Scroll View 仍然不知道高度应该是多少,现在你可以调整大小,scroll view 将传递他的内容的大小(如果内容小于最大大小) .

因此,为了解决这两个问题,我在滚动视图和滚动视图

的内部视图中添加了一个具有较小优先级的等高

希望对您有所帮助。

可以在界面生成器中使用自动布局轻松完成。

  1. 使用 "less than or equal" 关系设置外部容器视图("Parent container for scrollview" 在您的示例中)高度约束。

2.add "equal heights" 约束内容视图和滚动视图。然后为这个约束设置低优先级。

就是这样。现在,如果内容高度发生变化,您的滚动视图将按高度调整大小,但仅限于外部视图高度约束限制的最大高度。

对于我的项目,我有类似的问题。您可以使用以下方法使其变通。

首先,标题和底部动作高度是固定的。内容具有可变高度。您可以使用 font-size 将其添加到 mainView 作为一个 child,然后调用 layoutIfNeeded,然后可以计算其高度并将其保存为 XX。然后将其从mainView中移除。

其次,使用normal constraint 布局content part with scrollView,mainView 的height constraint为XX,setContentCompressionResistancePriority(.defaultLow, for: .vertical)。

最后,alert 可以在内容较短时显示精确大小,而在内容较长且滚动时显示有限大小。

我已经能够仅使用 AutoLayout 约束实现这种确切的行为。这是一个如何操作的通用演示:它可以按您认为合适的方式应用于您的视图层次结构。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let kTestContentHeight: CGFloat = 1200

        // Subview that will shrink to fit content and expand up to 50% of the view controller's height
        let modalView = UIView()
        modalView.backgroundColor = UIColor.gray

        // Scroll view that will facilitate scrolling if the content > 50% of view controller's height
        let scrollView = UIScrollView()
        scrollView.backgroundColor = .yellow

        // Content which has an intrinsic height
        let contentView = UIView()
        contentView.backgroundColor = .green


        // add modal view
        view.addSubview(modalView)
        modalView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([modalView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                     modalView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                     modalView.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor,
                                                                       multiplier: 0.5),
                                     modalView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])

        let expandHeight = modalView.heightAnchor.constraint(equalTo: view.heightAnchor)
        expandHeight.priority = UILayoutPriority.defaultLow
        expandHeight.isActive = true

        // add scrollview to modal view
        modalView.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([scrollView.topAnchor.constraint(equalTo: modalView.topAnchor),
                                     scrollView.leftAnchor.constraint(equalTo: modalView.leftAnchor),
                                     scrollView.rightAnchor.constraint(equalTo: modalView.rightAnchor),
                                     scrollView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)])


        // add content to scrollview
        scrollView.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
                                     contentView.widthAnchor.constraint(equalTo: modalView.widthAnchor),
                                     contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
                                     contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
                                     contentView.heightAnchor.constraint(equalToConstant: kTestContentHeight)])
        let contentBottom = contentView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)
        contentBottom.priority = .defaultLow
        contentBottom.isActive = true
    }

}

您可以通过两个约束相当简单地做到这一点

  • 约束 1:ScrollView.height <= MAX_SIZE。优先级 = 必需
  • 约束 2:ScrollView.height = ScrollView.contentSize.height。优先级 = 默认高

AutoLayout 将 'try' 将 scrollView 保持为 contentSize,但 'give up' 当它匹配最大高度时将停在那里。

唯一棘手的部分是设置约束 2 的高度。

当我的 UIStackView 在 UIViewController 中时,我在 viewWillLayoutSubviews

如果您通过子类化 UIScrollView 来实现这一点,您可以在 updateConstraints

中完成

类似

override func viewWillLayoutSubviews() {

    scrollViewHeightConstraint?.constant = scrollView.contentSize.height     

    super.viewWillLayoutSubviews()
}