【问题标题】:Enabling NSScrollView to scroll its contents using Auto Layout in Interface Builder使用 Interface Builder 中的 Auto Layout 启用 NSScrollView 滚动其内容
【发布时间】:2015-03-24 19:43:04
【问题描述】:

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

【问题讨论】:

    标签: macos cocoa interface-builder autolayout nsscrollview


    【解决方案1】:

    这是一般方法:

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

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

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

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

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


    更新:

    还有另一种方法。配置文档视图中的约束,使其具有严格的大小(没有不等式)。这通常是从其顶部到顶部子视图、从该子视图的底部到另一个子视图的顶部等以及从底部子视图的底部到文档视图的底部的一系列约束。导致尾随也是如此。

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

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

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

    所以,最后一步是创建NSClipView 的子类,它覆盖-isFlipped 以返回YES。然后,将 NIB 中的剪辑视图的类设置为您的子类。之后,它将按您的意愿工作。

    【讨论】:

    • 谢谢!我已遵循您的建议,但 IB 为 Clip View.Bottom
    • 根据您做事的顺序,这可能会发生,但在您设置其他约束之前应该只是暂时的。您有固定滚动视图本身大小的约束,对吗?并且您有有效地确定文档视图大小的约束(它至少足够大以包含其子视图加上低优先级约束以使其尽可能小)?而且它和clip view之间除了Bottom不等式还有Top、Leading、Trailing约束?
    • 谢谢,我没有在滚动视图本身上加回底部约束。在我补充说它工作得很好之后。非常感谢!
    • 不客气。我刚刚用另一种方法更新了我的答案,如果您不能让文档视图增长以填充剪辑视图,这种方法可能更直观或更可行。
    • 对我来说,当使用您更新的技术时,它适用于翻转的文档视图而不是剪辑视图。我真的不明白为什么,因为我同意你的推理。翻转剪辑视图给了我奇怪的行为。
    【解决方案2】:

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

    【讨论】:

    • 如果您在 Objective C 项目中,这也适用。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我将在https://stackoverflow.com/a/49947440/2846508 重新发布我的答案。

      观看 Apple 2012 年开发者大会关于 Auto Layout 的视频,了解有关在代码中使用 Auto Layout 的信息。

      只需在 Interface Builder 或代码中使用我在本视频教程中记录的方法:

      How to use NSScrollView with Auto Layout

      这是我在本视频中使用的方法:

      1. 窗口 -- 设置委托和 IBOutlet 属性

      2. ScrollView -- 固定边缘,无边框,不绘制背景

      3. documentView -- 固定边缘 0,然后是另一个尾部和底部,clipView ≥ 0 @499 和 clipView ≤ 0 @501 用于 documentView 的尾部和底部约束

      4. 水平堆栈视图中的标签和文本字段,垂直堆栈视图中

      5. 垂直堆栈视图固定边缘默认,然后另一个底部,底部≤默认@499和≥默认@750

      6. 水平堆栈视图前导和尾随固定 0

      7. 标签和文本字段将 Y 中心对齐到水平堆栈视图

      8. 文本字段顶部和底部和尾部 2 @750,宽度 ≥ 100,高度 ≥ 22

      9. 后续水平堆栈视图前导和尾随固定,对齐文本字段前导

      【讨论】:

        【解决方案5】:

        为了改进 Ken 的回答,我这样做了:

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

        • 然后我做了以下事情:

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

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

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

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

        • 在我嵌入到 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
        

        【讨论】:

          【解决方案6】:

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

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

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

          我是这样做的:

          1. NSScrollView设置以下约束

            • 引领空间走向容器
            • 到容器的顶部空间
            • 等宽
            • 等高

          如下图

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

            如果您想这样做,在您更改 NSScrollView 的帧大小后,视图也将保持其大小。

          就我而言,它就像魅力一样!我在NSClipView 中有一个ImageView,并且在您调整窗口大小后它也会滚动。希望它有效!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-02-12
            • 2013-06-15
            • 1970-01-01
            • 1970-01-01
            • 2016-01-20
            • 2015-03-03
            • 2019-10-04
            • 2011-03-13
            相关资源
            最近更新 更多