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


当前回答

正如Eik所说,UIStackView和UIScrollView很好地结合在一起。

关键是UIStackView处理不同内容的可变高度/宽度,然后UIScrollView做它的工作很好地滚动/反弹内容:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)       
}

其他回答

在我的例子中,stackView内的视图数量是可变的,我想将项目居中。例如,在stackView中有一个视图,我希望这个视图位于屏幕中央,如果所有的视图都不适合屏幕,我希望视图是可滚动的。

这是我的视图的层次结构。

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

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

对于containerView:

拖尾,领先,底部,顶部,等高,等宽(250优先级)到superview 固定的高度

对于scrollView:

尾随,领先,底部,顶部到父视图

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

如果有人正在寻找一个没有代码的解决方案,我创建了一个例子来完成这完全在故事板,使用自动布局。

你可以从github上得到。

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

创建一个UIScrollView,并设置它的约束。 添加一个UIStackView到UIScrollView 设置约束:前导,后尾,顶部和底部应该等于UIScrollView中的约束 在UIStackView和UIScrollView之间设置一个等宽约束。 在UIStackView上设置轴=垂直,对齐=填充,分布=等间距,间距= 0 添加一些uiview到UIStackView 运行

在第4步中将宽度交换为高度,并在第5步中设置轴=水平,以获得水平的UIStackView。

这里投票最多的答案中的约束条件对我来说是有效的,我已经将在我的故事板中创建的约束条件的图像粘贴在下面。

我确实提到了两个问题,但其他人应该注意:

After adding constraints similar to those in in the accepted answer, I'd get the red autolayout error Need constraints for: X position or width. This was solved by adding a UILabel as a subview of the stack view. I'm adding the subviews programmatically, so I originally had no subviews on the storyboard. To get rid of the autolayout errors, add a subview to the storyboard, then remove it on load before adding your real subviews and constraints. I originally attempted to add UIButtons to the UIStackView. The buttons and views would load, but the scroll view would not scroll. This was solved by adding UILabels to the Stack View instead of buttons. Using the same constraints, this view hierarchy with the UILabels scrolls but the UIButtons does not. I'm confused by this issue, as the UIButtons do seem to have an IntrinsicContentSize (used by the Stack View). If anyone knows why the buttons don't work, I'd love to know why.

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

正如Eik所说,UIStackView和UIScrollView很好地结合在一起。

关键是UIStackView处理不同内容的可变高度/宽度,然后UIScrollView做它的工作很好地滚动/反弹内容:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)       
}

水平滚动(UIScrollView中的UIStackView)

用于水平滚动。首先,创建一个UIStackView和一个UIScrollView,并按以下方式将它们添加到您的视图中:

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

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

记住在UIStackView和UIScrollView上设置translatesAutoresizingMaskIntoConstraints为false:

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

但是UIStackView的宽度锚点必须等于或大于UIScrollView锚点的宽度:

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。

文本Insets

另一个你可能会发现有用的特性是,因为UIStackView保存在UIScrollView中,你现在可以访问文本insets,使事情看起来更漂亮一点。

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