使用 Interface Builder 中的自动布局启用 NSScrollView 以滚动其内容

Enabling NSScrollView to scroll its contents using Auto Layout in Interface Builder

我实现了一个自定义 NSView,其中包含许多 NSTextField 和其他 NSView。然后我使用 Editor > Embed In > Scroll View 将该自定义视图嵌入到滚动视图中。这将创建在大纲中可见的适当层次结构,但我需要添加自动布局约束以指定此滚动视图应放置在视图中的位置(顶部、底部、前导、尾随)。此外,我必须为自定义视图添加约束,针对剪辑视图进行设置,以便将元素布置在正确的位置。这很有效,当我 运行 应用程序所有元素都正确显示并且视图确实弹跳滚动时。但是,当我降低主视图的高度以使并非所有元素都适合屏幕时,会出现自动布局警告,并且当我更新框架时它会再次增加视图的高度。为了解决这个问题,我不得不删除滚动视图对主视图的底部约束。现在,当我 运行 应用程序时,window 设置为正确的大小,但我无法滚动自定义视图以到达内容的底部 - 它受到限制,因此根本不会滚动除了弹性弹跳效果,因为你处于边缘极限。所以我的问题是,当我在 XIB 中布置所有元素并使用自动布局时,我必须做什么才能允许滚动视图滚动?

这是一般方法:

  • 使文档视图至少与剪辑视图一样高。或者,等效地,使剪辑视图不高于文档视图。
  • 允许文档视图增加高度,但不能缩小超出其子视图的要求。
  • 通过具有低优先级约束以在给定其他约束的情况下使其尽可能小来防止文档视图中的歧义。

因此,例如,剪辑视图的底部和文档视图的底部之间应该有一个约束,但它应该是一个不等式:Superview.Bottom <= Document View.Bottom。 (或者,等效地,文档 View.Bottom >= Superview.Bottom。)

在文档视图中,您可能在底部有一些文本字段或其他内容,并且在它和文档视图的底部之间有一个约束。使该约束成为不等式:Superview.Bottom >= 文本 Field.Bottom + 标准间距。

这将导致文档视图的高度不明确。它可以是任何大小,足以容纳它的所有子视图。添加高度约束。将其优先级设置为 51 并将其常量设置为 0。也就是说,它想让视图的高度为 0,但优先级非常低,因此几乎任何其他东西都会取代它。但它解决了歧义。

如果你想允许水平滚动,你需要在水平方向做同样的事情。


更新:

还有另一种方法。在文档视图中配置约束以赋予它严格的大小(没有不平等)。这通常是从其顶部到顶部子视图,从该子视图的底部到另一个子视图的顶部等,以及从底部子视图的底部到文档视图底部的约束链。前导尾随相同。

然后,剪辑视图和文档视图之间唯一必要的约束是顶部约束和前导约束。

如果您在此配置中进行测试,您将能够调整大小并且滚动视图将滚动。所以,这很好。但是,当滚动视图的内容区域高于文档视图时,文档视图将被固定到内容区域的底部。在那种情况下,您通常希望它固定在顶部。

原因是剪辑视图没有翻转。此外,它正在调整其边界以匹配文档视图。因此,即使存在将文档视图固定到剪辑视图顶部的约束,剪辑视图的顶部也不是您期望的位置。剪辑视图将文档视图放在底部的 (0, 0)。

因此,最后一部分是创建 NSClipView 的子 class,覆盖 -isFlipped 到 return YES。然后,将NIB中剪辑视图的class设置为你的subclass。之后,它会如你所愿。

我要重新post我在的回答。

Look at Apple's 2012 developer conference videos about Auto Layout for information about using Auto Layout in code.

Simply use in Interface Builder or in code the approach I recorded in this video tutorial:

How to use NSScrollView with Auto Layout

This is the approach I used in this video:

  1. Window -- set delegate and IBOutlet property

  2. ScrollView -- fixed edges, no border, don't draw background

  3. documentView -- fixed edges 0, then another trailing and bottom, clipView ≥ 0 @499 and clipView ≤ 0 @501 for both trailing and bottom constraints to documentView

  4. label and text field in horizontal stack view, in vertical stack view

  5. vertical stack view fixed edges default, then another bottom, bottom ≤ default @499 and ≥ default @750

  6. horizontal stack view leading and trailing fixed 0

  7. label and text field align Y center to horizontal stack view

  8. text field top and bottom and trailing 2 @750, width ≥ 100, height ≥ 22

  9. subsequent horizontal stack views leading and trailing fixed, align text field leadings

基于 Ken Thomases 的回答,Swift 4,可以将 flipped 作为用户定义的属性添加到 NSClipView,以将内容固定到滚动视图的顶部。

为了改进 Ken 的回答,我做了以下工作:

• 首先,我阅读了这篇关于如何使用自动布局在 Interface Builder (IB) 中设置 NSScrollView 的精彩文章tutorial

• 然后我执行了以下操作:

• 在 IB 中创建了一个 NSScrollView 实例;

• 创建了 NSClipView 的新子类并添加了以下代码:

class ScCalendarClipView: NSClipView {
    open override var isFlipped: Bool {
        return true
    }
} 

• 创建了自定义文档视图并将其作为 NSScrollView 实例的子项嵌入到 IB 中。

• 在我嵌入 NSScrollView 实例的视图中的 IB 中添加了以下约束(我们在这里称之为:DocumentView

// Right
First Item: SuperView.Trailing
Relation: Less Than or Equal
Second Item: DocumentView.Trailing
Constant: 0
Priority: 510
Multiplier: 1

First Item: SuperView.Trailing
Relation: Greater Than or Equal
Second Item: DocumentView.Trailing
Constant: 0
Priority: 490
Multiplier: 1

// Left
First Item: DocumentView.Leading
Relation: Equal
Second Item: SuperView.Leading
Constant: 0
Priority: 1000
Multiplier: 1

// Bottom (negative constants increase the size of the document view)
First Item: SuperView.Bottom
Relation: Less Than or Equal
Second Item: DocumentView.Bottom
Constant: -600
Priority: 510
Multiplier: 1

// Bottom (constant should be equal to the bottom constraint above)
First Item: SuperView.Bottom
Relation: Greater Than or Equal
Second Item: DocumentView.Bottom
Constant: -600
Priority: 490
Multiplier: 1

// Top
First Item: DocumentView.Top
Relation: Equal
Second Item: SuperView.Top
Constant: 0
Priority: 1000
Multiplier: 1

NSScrollView 需要有 X、Y、宽度和高度约束,这样即使您在 NSClipView 中有比 ScrollView 更大 width/height 的视图,它也能保持框架不变。

我看到如果你有更大尺寸的视图并且你没有这些限制,NSScrollView 的框架会自动设置为与 NSClipView 相同,这将是一个更大的框架,它可能会超过Window 的框架大小。

这就是为什么有时 NSScrollView 看起来不滚动,但实际上它足够大,包含所有视图,超过 Window 的框架,并且不需要滚动滚动。

我通过这样做让它工作了:

  1. NSScrollView设置以下约束

    • 引导Space到集装箱
    • 顶部 Space 到容器
    • 等宽
    • 等高

如下图

  1. NSClipView

    中的每个视图执行相同的操作

    如果您想要更改 NSScrollView 的框架大小,视图也将保持其大小。

在我的案例中,它非常有效!我在 NSClipView 中有一个 ImageView,它也会在您调整 window 的大小后滚动。希望有用!

对于像我一样难以在文档视图和剪辑视图之间设置约束的人: 确保剪辑视图设置为自动布局。 由于神秘的原因,默认情况下它没有设置为自动(至少 Xcode 11)。 Cfr 下面尺寸检查器的截图。