UIStackView是否可以滚动?

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

156450 次浏览

正如Eik所说,UIStackViewUIScrollView很好地结合在一起,参见在这里

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

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

只需将其添加到viewdidload:

let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
< p >来源: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html < / p >

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

你可以从github中得到它。

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

  1. 创建UIScrollView,并设置其约束。
  2. UIScrollView . properties中添加UIStackView
  3. 设置约束:LeadingTrailingTop &Bottom应该等于UIScrollView中的值
  4. UIStackViewUIScrollView之间设置一个相等的Width约束。
  5. UIStackView上设置轴=垂直,对齐=填充,分布=相等间距,并且间距= 0
  6. UIStackView中添加一个UIViews
  7. 运行

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

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

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

  1. 在添加了类似于接受的答案中的约束后,我会得到红色的自动布局错误Need constraints for: X position or width。这是通过添加一个UILabel作为堆栈视图的子视图来解决的。

    我通过编程方式添加子视图,所以故事板上最初没有子视图。要消除自动布局错误,请向故事板添加子视图,然后在加载时删除它,然后再添加真正的子视图和约束

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

    我对这个问题感到困惑,因为UIButtons 似乎有一个IntrinsicContentSize(由堆栈视图使用)。如果有人知道为什么按钮不工作,我很想知道为什么

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

constraints for stack view in scroll view[1]

Apple的自动布局指南包括使用滚动视图的整个部分。一些相关片段:

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

此外:

你的布局必须完全定义内容视图的大小(除了 . ...当内容视图比滚动视图高时,滚动视图允许垂直滚动。当内容视图比滚动视图宽时,滚动视图允许水平滚动

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

// 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

如果你有一个约束,使堆栈视图垂直居中在滚动视图内,只需删除它。

到2020年为止。

100%故事板或100%代码。

这个例子是垂直的:


下面是最简单的解释:

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

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

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

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

为了清晰,标签的背景颜色为红色。设置标签高度为100。

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

    令人惊讶的是,control-drag从UILabel拖到滚动视图,从拖到堆栈视图,然后选择相同的宽度

重复:

不要从UILabel控件拖动到UILabel的父控件,而是拖到祖父控件。(换句话说,一直到滚动视图,不要到堆栈视图。)

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

秘密提示——苹果臭虫:

将不工作只有一个项目!添加一些标签以使演示正常工作。

你就完成了。

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


另一种方法:

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

这里有另一种方法……

从堆栈视图拖动到滚动视图,并添加“宽度等于”;约束。这看起来很奇怪,因为你已经左右为难了,但是这就是你要做的。不管看起来有多奇怪,这就是秘密。

所以你有两个选择:

  1. 令人惊讶的是,将堆栈视图中每个项的宽度设置为scrollview祖父类的宽度(不是stackview父元素)。

  1. 令人惊讶的是,设置一个“宽度相等”;将stackview的左边和右边固定在scrollview上。

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

你可以尝试ScrollableStackView: https://github.com/gurhub/ScrollableStackView

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

样本代码(Swift)

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];
// ...
    首先,设计你的视图,最好是在Sketch或get an之类的东西中 你想要的滚动内容。

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

    之后,在视图控制器中放入一个滚动视图,这是a 逻辑,我发现在iOS中几乎所有的时间都是有效的(它可能需要通过该视图类的文档,可以通过命令+单击该类或通过谷歌获得)

    如果您正在处理两个或多个视图,那么首先从一个已经较早引入或更原始的视图开始,然后转到已引入的视图 或者更现代。既然滚动视图已经 首先介绍,首先从滚动视图开始,然后转到 堆栈视图。这里将滚动视图约束在相对于它的父视图的所有方向上设置为零。把你所有的视图都放在这个滚动视图中,然后把它们放在堆栈视图中

当使用堆栈视图时

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

  • 之后调整堆栈视图的属性。如果仍然没有实现所需的视图,则使用另一个堆栈视图。

  • 如果仍然没有达到,那么玩抗压缩或内容拥抱优先级。
  • 在此之后,向堆栈视图添加约束。
  • 也可以考虑使用一个空的UIView作为填充视图,如果以上都没有给出令人满意的结果。
在创建视图后,在母堆栈视图和滚动视图之间放置一个约束,同时约束子堆栈视图与母堆栈视图。 希望这个时候它能正常工作,否则你可能会从Xcode得到一个警告,给出建议,阅读它所说的并执行这些建议。希望现在你应该有一个工作视图,正如你的期望:)

垂直堆栈视图/滚动视图的示例(使用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)
]

只要确保每个子视图的高度都已定义!

我正在寻找做同样的事情,无意中发现了这个优秀的文章。如果你想使用锚API以编程方式做到这一点,这是一种方法。

总之,将你的UIStackView嵌入到你的UIScrollView中,并设置UIStackView的锚约束以匹配UIScrollView的锚约束:

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

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

在UIStackView和UIStackView之间设置一个等宽约束 UIScrollView . < / p >

enter image description here

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

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()
}

水平滚动(UIScrollView中的UIStackView)

用于水平滚动。首先,创建UIStackViewUIScrollView,并按以下方式将它们添加到视图中:

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


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

记住在UIStackViewUIScrollView上将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的width锚定必须等于或大于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中,所以你现在可以访问文本嵌入,让事情看起来更漂亮一些。

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

在你的场景中放置一个滚动视图,并调整它的大小,使它充满场景。然后,在滚动视图中放置一个堆栈视图,并在堆栈视图中放置添加项按钮。一旦一切就绪,就设置以下约束条件:

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

enter image description here

Stack View.Width = Scroll View.Width是键。

为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的垂直可滚动视图。对于水平对,相应更改代码。

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

我给你一个正确的解决方案

对于Xcode 11+

<强>步骤1: 添加滚动视图并将其大小调整为

enter image description hereenter image description here

<强>步骤2: 为ScrollView

添加约束

enter image description here

<强>步骤3: 在ScrollView中添加StackView,并调整它的大小

enter image description here

<强>步骤4: 为StackView (Stask View ->内容布局指南->“领先,上,后,下”;)

添加约束

enter image description here enter image description here < / p >

<强>步骤4.1: 正确约束->常数(…→常数= 0)

enter image description here enter image description here < / p >

<强>第五步: 为StackView (Stask View ->框架布局指南->“平等的宽度")

添加约束

enter image description here enter image description here < / p >

步骤6示例:HeightConstraints运行添加两个UIView enter image description here enter image description here < / p >

希望对大家有用

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

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

enter image description here

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

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

enter image description here

对于containerView:

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

enter image description here

对于滚动视图:

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

scrollView也嵌入到具有前导和后导约束的视图中。

enter image description here

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

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

将stackview中的动态元素嵌入到scrollview中的一种简单方法。在XIB中,在UIScrollView中添加一个UIStackView,并添加stackview适合scrollview的约束(top, bottom, lead, trail),并添加一个约束来匹配它们之间的水平中心。但要标明“在构建时移除”;最后一个约束条件。它使XIB高兴,避免错误。

示例: 约束条件 < / p > < p >:  buildtime remove < / p >

然后在你的代码中,像这样在你的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
}
当使用Storyboard执行此操作时,每个人似乎都错过了FIX THE MULTIPLIER! 当你按照上面的步骤在任何人教程和重置常数为0,同时检查乘数,并将其重置为1,它将采取了一些其他因素时,视觉链接保持在原地

我发现我可以在UIStackView中简单地做一个loooooooooong文本块挤压或拉伸

< p >简单 向滚动视图添加约束 左上右下,cons 0

向指向滚动条Content Layout Guide的堆栈视图添加约束

然后从框架布局指南中添加等宽或等高的约束。 选择:宽度如果内容需要垂直滚动,高度如果内容需要水平滚动

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

如果你不这样做,它就会摇摇欲坠

如果它工作,你可以点击内部内容和鼠标滚动