253

假设我在 UIStackView 中添加了更多可以显示的视图,我该如何进行UIStackView滚动?

4

21 回答 21

693

如果有人正在寻找没有代码的解决方案,我创建了一个示例,使用自动布局在情节提要中完全做到这一点。

您可以从github获取它。

基本上,要重新创建示例(用于垂直滚动):

  1. 创建一个UIScrollView,并设置它的约束。
  2. 添加一个UIStackViewUIScrollView
  3. 设置约束:Leading, Trailing, Top&Bottom应该等于来自UIScrollView
  4. 在和之间设置相等Width约束。UIStackViewUIScrollView
  5. UIStackView
  6. 将数量添加UIViewsUIStackView

在步骤 4 中交换WidthHeight并在步骤 5 中设置Axis= Horizontal,以获得水平 UIStackView。

于 2016-02-01T17:06:07.260 回答
54

Apple 的 Auto Layout Guide 包括关于使用 Scroll Views的整个部分。一些相关的片段:

  1. 将内容视图的顶部、底部、前导和后沿固定到滚动视图的相应边缘。内容视图现在定义了滚动视图的内容区域。
  2. (可选)要禁用水平滚动,请将内容视图的宽度设置为等于滚动视图的宽度。内容视图现在水平填充滚动视图。
  3. (可选)要禁用垂直滚动,请将内容视图的高度设置为等于滚动视图的高度。内容视图现在水平填充滚动视图。

此外:

您的布局必须完全定义内容视图的大小(步骤 5 和 6 中定义的除外)。… 当内容视图高于滚动视图时,滚动视图启用垂直滚动。当内容视图比滚动视图宽时,滚动视图启用水平滚动。

总而言之,滚动视图的内容视图(在本例中为堆栈视图)必须固定在其边缘,并且必须限制其宽度和/或高度。这意味着堆栈视图的内容必须(直接或间接)在需要滚动的方向上受到约束,例如,这可能意味着向垂直滚动堆栈视图内的每个视图添加高度约束。以下是如何允许垂直滚动包含堆栈视图的滚动视图的示例:

// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
于 2016-09-12T17:20:55.173 回答
42

我为您提供正确的解决方案

对于 Xcode 11+

第 1 步: 添加ScrollView并调整其大小

在此处输入图像描述在此处输入图像描述

第 2 步:为 ScrollView 添加约束

在此处输入图像描述

第 3 步:StackView添加到 ScrollView,并调整其大小。

在此处输入图像描述

第 4 步: 为 StackView 添加约束(Stask View -> Content Layout Guide -> “Leading, Top, Trailing, Bottom”

在此处输入图像描述 在此处输入图像描述

步骤 4.1: 正确的约束 ->常量(... -> 常量 = 0)

在此处输入图像描述 在此处输入图像描述

第 5 步: 为 StackView 添加约束(Stask View -> Frame Layout Guide -> “Equal Widths ”)

在此处输入图像描述 在此处输入图像描述

第 6 步示例:使用HeightConstraintsRUN 添加两个 UIView 在此处输入图像描述 在此处输入图像描述

我希望它对你有用

于 2020-07-22T19:16:56.977 回答
18

此处投票最多的答案中的约束对我有用,并且我粘贴了下面的约束图像,就像在我的故事板中创建的那样。

我确实遇到了两个问题,但其他人应该知道:

  1. 添加类似于已接受答案中的约束后,我会得到红色的 autolayout error Need constraints for: X position or width。这是通过添加 UILabel 作为堆栈视图的子视图来解决的。

    我以编程方式添加子视图,所以我最初在情节提要上没有子视图。要摆脱自动布局错误,请在情节提要中添加一个子视图,然后在加载时将其删除,然后再添加真正的子视图和约束。

  2. 我最初试图添加UIButtons到 UIStackView。按钮和视图会加载,但滚动视图不会滚动。这是通过添加UILabels到堆栈视图而不是按钮来解决的。使用相同的约束,带有 UILabel 的视图层次结构会滚动,但 UIButtons 不会。

    我对这个问题感到困惑,因为 UIButtons似乎确实有一个 IntrinsicContentSize (由 Stack View 使用)如果有人知道为什么按钮不起作用,我很想知道为什么。

这是我的视图层次结构和约束,供参考:

滚动视图中堆栈视图的约束[1]

于 2016-04-22T18:24:15.530 回答
18

更新到 2020 年。

100% 故事板或 100% 代码。

这个例子是垂直的:


这是最简单的解释:

  1. 有一个空白的全屏场景

  2. 添加滚动视图。按住 Control 键从滚动视图拖动到基本视图,添加左-右-上-下,全部为零。

  3. 在滚动视图中添加堆栈视图。按住 Control 从堆栈视图拖动到滚动视图,添加左-右-上-下,全部为零。

  4. 在堆栈视图中放置两个或三个标签。

为清楚起见,将标签的背景颜色设为红色。将标签高度设置为 100。

  1. 现在设置每个 UILabel 的宽度

    令人惊讶的是,从UILabel到滚动视图,而不是堆栈视图,并选择相等的宽度

重复:

不要控制从 UILabel 拖动到 UILabel 的父级 - 转到祖父级。(换句话说,一直到滚动视图,不要到堆栈视图。)

就是这么简单。这就是秘密。

秘密提示 - 苹果错误:

不适用于仅一项!添加一些标签以使演示工作。

你完成了。

提示:您必须为每个新项目添加高度。任何滚动堆栈视图中的每个项目都必须具有固有大小(例如标签)或添加显式高度约束。


替代方法:

回顾一下上述方法:令人惊讶的是,将标签的宽度设置为滚动视图(而不是堆栈视图)的宽度。

这是另一种方法...

从堆栈视图拖动到滚动视图,并添加“宽度相等”约束。这看起来很奇怪,因为您已经将左右固定,但您就是这样做的。不管它看起来多么奇怪,这就是秘密。

所以你有两个选择:

  1. 令人惊讶的是,将堆栈视图中每个项目的宽度设置为滚动视图祖父母(而不是堆栈视图父)的宽度。

或者

  1. 令人惊讶的是,将堆栈视图的“宽度相等”设置为滚动视图 -即使您确实将堆栈视图的左右边缘固定到滚动视图。

需要明确的是,使用其中一种方法,不要同时使用两种方法。

于 2016-12-05T15:18:35.917 回答
16

正如 Eik 所说,UIStackView一起UIScrollView玩得很好,看这里

关键是UIStackView处理不同内容的可变高度/宽度,UIScrollView然后在滚动/弹跳该内容方面做得很好:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)       
}
于 2015-07-29T05:05:01.520 回答
12

我想做同样的事情,偶然发现了这篇优秀的帖子。如果您想使用锚 API 以编程方式执行此操作,这就是要走的路。

总而言之,将你的嵌入你UIStackViewUIScrollView,并设置锚约束UIStackView以匹配那些UIScrollView

stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
于 2017-04-14T09:05:23.313 回答
11

水平滚动(UIScrollView 中的 UIStackView)

用于水平滚动。首先,创建 aUIStackView和 aUIScrollView并将它们添加到您的视图中,方法如下:

let scrollView = UIScrollView()
let stackView = UIStackView()

scrollView.addSubview(stackView)
view.addSubview(scrollView)

记住在和 上设置translatesAutoresizingMaskIntoConstraintsto :falseUIStackViewUIScrollView

stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false

为了使一切正常,尾随、前导、顶部和底部锚点UIStackView应等于UIScrollView锚点:

stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

但是anchor的宽度UIStackView必须等于或大于UIScrollViewanchor的宽度:

stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true

现在锚定你的UIScrollView,例如:

scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true 

接下来,我建议尝试以下UIStackView对齐和分布设置:

topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center

topicStackView.spacing = 10

最后,您需要使用该addArrangedSubview:方法将子视图添加到您的 UIStackView。

文字插图

您可能会发现另一个有用的功能是,因为UIStackView保存在 a 中UIScrollView,您现在可以访问文本插入以使事物看起来更漂亮一些。

let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset

// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true
于 2019-02-07T10:21:42.600 回答
6

只需将其添加到viewdidload

let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets

来源: https ://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html

于 2015-12-15T13:35:28.370 回答
2

您可以尝试ScrollableStackViewhttps ://github.com/gurhub/ScrollableStackView

它是 Objective-C 和 Swift 兼容的库。它可以通过CocoaPods 获得

示例代码(斯威夫特)

import ScrollableStackView

var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)

// add your views with 
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...

示例代码(Objective-C)

@import ScrollableStackView

ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];

UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];

// add your views with
[scrollable.stackView addArrangedSubview:rectangle]; 
// ...
于 2016-12-20T19:52:21.097 回答
2

为 macOS Catalyst 添加一些新视角。由于 macOS 应用程序支持窗口大小调整,因此您可能UIStackView会从不可滚动状态转换为可滚动状态,反之亦然。这里有两件微妙的事情:

  • UIStackView旨在适应所有区域。
  • 在过渡期间,UIScrollView将尝试调整其边界以考虑导航栏(或 macOS 应用程序中的工具栏)下方新获得/丢失的区域。

不幸的是,这将创建一个无限循环。我对UIScrollView和 它不是很熟悉adjustedContentInset,但是从我登录它的layoutSubviews方法来看,我看到了以下行为:

  • 一放大窗户。
  • UIScrollView尝试缩小其边界(因为不需要工具栏下方的区域)。
  • UIStackView跟随。
  • 不知何故UIScrollView不满意,决定恢复到更大的界限。这让我感觉很奇怪,因为我从日志中看到的是UIScrollView.bounds.height == UIStackView.bounds.height.
  • UIStackView跟随。
  • 然后循环到第 2 步。

在我看来,两个步骤可以解决这个问题:

  • 对齐UIStackView.topUIScrollView.topMargin
  • 设置contentInsetAdjustmentBehavior.never

在这里,我关心的是一个垂直可滚动的视图,它具有垂直增长的UIStackView. 对于水平对,相应地更改代码。

希望它对将来的任何人有所帮助。在互联网上找不到任何人提到这一点,我花了很长时间才弄清楚发生了什么。

于 2020-05-18T00:44:11.420 回答
1

如果有人在寻找水平滚动视图

func createHorizontalStackViewsWithScroll() {
    self.view.addSubview(stackScrollView)
    stackScrollView.translatesAutoresizingMaskIntoConstraints = false
    stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
    stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
    stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
    stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
    stackScrollView.addSubview(stackView)
    
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
    stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
    stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
    stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
    stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
    
    stackView.distribution = .equalSpacing
    stackView.spacing = 5
    stackView.axis = .horizontal
    stackView.alignment = .fill
    
    for i in 0 ..< images.count {
        let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
        // set button image
        photoView.translatesAutoresizingMaskIntoConstraints = false
        photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
        photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
        
        stackView.addArrangedSubview(photoView)
    }
    stackView.setNeedsLayout()
}
于 2019-01-27T09:01:59.973 回答
0

如果您有约束将 Stack View 在滚动视图内垂直居中,只需将其删除。

于 2016-10-10T15:57:07.420 回答
0
  1. 首先设计你的视图,最好是像 Sketch 这样的东西,或者了解你想要什么作为可滚动内容。

  2. 在此之后使视图控制器自由形式(从属性检查器中选择)并根据视图的内在内容大小设置高度和宽度(从尺寸检查器中选择)。

  3. 之后在视图控制器中放置一个滚动视图,这是一个逻辑,我发现它在 iOS 中几乎一直都在工作(它可能需要通过该视图类的文档,可以通过命令 + 单击获取该课程或通过谷歌搜索)

    如果您正在使用两个或多个视图,那么首先从一个较早引入或更原始的视图开始,然后转到稍后引入或更现代的视图。所以这里既然先介绍了滚动视图,那就先从滚动视图开始,然后再进入堆栈视图。这里将滚动视图约束在所有方向上相对于其超级视图都为零。将所有视图放入此滚动视图中,然后将它们放入堆栈视图中。

使用堆栈视图时

  • 首先从地面开始(自下而上的方法),即,如果您的视图中有标签、文本字段和图像,则首先布置这些视图(在滚动视图内),然后将它们放入堆栈视图。

  • 之后调整堆栈视图的属性。如果仍未获得所需的视图,则使用另一个堆栈视图。

  • 如果仍未实现,则使用抗压缩性或内容拥抱优先级。
  • 在此之后向堆栈视图添加约束。
  • 如果以上所有方法都没有给出令人满意的结果,还可以考虑使用空的 UIView 作为填充视图。

制作视图后,在母堆栈视图和滚动视图之间放置一个约束,同时约束子堆栈视图与母堆栈视图。希望到这个时候它应该可以正常工作,或者您可能会从 Xcode 收到警告,给出建议,阅读它所说的内容并实施这些建议。希望现在您应该按照您的期望有一个工作视图:)。

于 2017-02-18T02:46:15.783 回答
0

垂直堆栈视图/滚动视图的示例(使用EasyPeasy自动布局):

let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
    Edges(),
    Width().like(self.view)
]

let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill    
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
    Edges(),
    Width().like(self.view)
]

只需确保定义了每个子视图的高度!

于 2017-02-24T10:34:59.417 回答
0

对于嵌套或单个 Stack 视图,滚动视图必须与根视图设置固定宽度。滚动视图内部的主堆栈视图必须设置相同的宽度。[我的滚动视图是视图忽略它的波纹管]

在 UIStackView 和 UIScrollView 之间设置相等的宽度约束。

在此处输入图像描述

于 2018-02-16T15:28:49.420 回答
0

在场景上放置一个滚动视图,并调整其大小以填充场景。然后,在滚动视图中放置一个堆栈视图,并将添加项目按钮放置在堆栈视图中。一切就绪后,设置以下约束:

Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width

在此处输入图像描述

代码:Stack View.Width = Scroll View.Width是关键。

于 2019-08-09T01:22:10.853 回答
0

在我的情况下,stackView 中的视图数量是可变的,我想将项目居中。因此,例如,对于 stackView 中的一个视图,我希望该视图位于屏幕中间,如果所有视图都不适合屏幕,我希望该视图是可滚动的。

这是我的观点的层次结构。

在此处输入图像描述

所以我为按钮设置了一个固定宽度,然后为stackView

  • 与按钮相同的固定宽度,但优先级为 650。
  • 将 X 中心对齐到 containerView
  • 尾随 >= 0 和前导 >= 0 到 containerView
  • 容器视图的底部和顶部空间

在此处输入图像描述

对于containerView

  • 尾随,前导,底部,顶部,等高,等宽(250优先级)到superview
  • 固定高度

在此处输入图像描述

对于滚动视图

  • 尾随、前导、底部、顶部到超级视图

scrollView 也嵌入在具有前导和尾随约束的视图中。

在此处输入图像描述

关于我用来解决这个问题的代码:

for index in 0...array.count - 1 {

      if index == 0 {
          firstButton.setTitle(title, for: .normal)
      } else {
          let button = UIButton()
          button.setTitle(title, for: .normal)
          stackView.addArrangedSubview(button)
          stackView.setNeedsLayout()
      }
}
containerView.layoutIfNeeded()
        
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.alignment = .fill
于 2021-03-25T16:35:54.283 回答
0

创建了在垂直和水平轴滚动上工作的示例项目。

https://gitlab.com/muhasturk/bmh.example.stackinsidescroll.ios

于 2021-03-31T14:25:42.173 回答
0

将stackview中的动态元素嵌入scrollview的一种简单方法。在 XIB 中,在 UIScrollView 内添加 UIStackView 并添加 stackview 适合滚动视图(顶部、底部、前导、尾随)的约束,并添加约束以匹配它们之间的水平中心。但最后一个约束标记为“在构建时删除”。它使 XIB 高兴并避免错误。

水平滚动示例: 约束

然后: 在构建时删除

然后在您的代码中,只需在 stackview 中添加按钮等元素,如下所示:

array.forEach { text in
     let button = ShadowButton(frame: .zero)
     button.setTitle(text, for: .normal)
     myStackView.addArrangedSubview(button)
     button.heightAnchor.constraint(equalToConstant: 40).isActive = true
     button.widthAnchor.constraint(equalToConstant: 80).isActive = true
}
于 2021-09-20T16:31:20.827 回答
-1

使用 Storyboard 执行此操作时,每个人似乎都错过了修复乘数!当您按照任何人教程中的上述步骤并将常量重置为 0 时,还会检查乘数并将其重置为 1,当视觉链接保持在原位时,它将采取一些其他因素

我发现我可以简单地在 UIStackView 中制作 LOOOOOOOOOOOONG 文本块壁球或拉伸

只需将约束添加到滚动视图左上右下,缺点 0

将约束添加到指向滚动条的堆栈视图内容布局指南

然后从 Frame Layout Guide 添加等宽或等高约束。Pick :如果内容需要垂直滚动,则为宽度,如果需要水平滚动,则为高度。

现在这里是关键。编辑每个约束并将常量重置为 0 并将乘数设置回 1!!!!!!!

如果你不这样做,它会变得很奇怪

如果有效,您可以单击内部内容并鼠标滚动

于 2021-12-31T13:05:11.797 回答