假设我在 UIStackView 中添加了更多可以显示的视图,我该如何进行UIStackView
滚动?
21 回答
如果有人正在寻找没有代码的解决方案,我创建了一个示例,使用自动布局在情节提要中完全做到这一点。
您可以从github获取它。
基本上,要重新创建示例(用于垂直滚动):
- 创建一个
UIScrollView
,并设置它的约束。 - 添加一个
UIStackView
到UIScrollView
- 设置约束:
Leading
,Trailing
,Top
&Bottom
应该等于来自UIScrollView
- 在和之间设置相等
Width
约束。UIStackView
UIScrollView
- 在
UIStackView
- 将数量添加
UIViews
到UIStackView
- 跑
在步骤 4 中交换Width
,Height
并在步骤 5 中设置Axis
= Horizontal
,以获得水平 UIStackView。
Apple 的 Auto Layout Guide 包括关于使用 Scroll Views的整个部分。一些相关的片段:
- 将内容视图的顶部、底部、前导和后沿固定到滚动视图的相应边缘。内容视图现在定义了滚动视图的内容区域。
- (可选)要禁用水平滚动,请将内容视图的宽度设置为等于滚动视图的宽度。内容视图现在水平填充滚动视图。
- (可选)要禁用垂直滚动,请将内容视图的高度设置为等于滚动视图的高度。内容视图现在水平填充滚动视图。
此外:
您的布局必须完全定义内容视图的大小(步骤 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
我为您提供正确的解决方案
对于 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 步示例:使用HeightConstraints和RUN 添加两个 UIView
我希望它对你有用
此处投票最多的答案中的约束对我有用,并且我粘贴了下面的约束图像,就像在我的故事板中创建的那样。
我确实遇到了两个问题,但其他人应该知道:
添加类似于已接受答案中的约束后,我会得到红色的 autolayout error
Need constraints for: X position or width
。这是通过添加 UILabel 作为堆栈视图的子视图来解决的。我以编程方式添加子视图,所以我最初在情节提要上没有子视图。要摆脱自动布局错误,请在情节提要中添加一个子视图,然后在加载时将其删除,然后再添加真正的子视图和约束。
我最初试图添加
UIButtons
到 UIStackView。按钮和视图会加载,但滚动视图不会滚动。这是通过添加UILabels
到堆栈视图而不是按钮来解决的。使用相同的约束,带有 UILabel 的视图层次结构会滚动,但 UIButtons 不会。我对这个问题感到困惑,因为 UIButtons似乎确实有一个 IntrinsicContentSize (由 Stack View 使用)。如果有人知道为什么按钮不起作用,我很想知道为什么。
这是我的视图层次结构和约束,供参考:
更新到 2020 年。
100% 故事板或 100% 代码。
这个例子是垂直的:
这是最简单的解释:
有一个空白的全屏场景
添加滚动视图。按住 Control 键从滚动视图拖动到基本视图,添加左-右-上-下,全部为零。
在滚动视图中添加堆栈视图。按住 Control 从堆栈视图拖动到滚动视图,添加左-右-上-下,全部为零。
在堆栈视图中放置两个或三个标签。
为清楚起见,将标签的背景颜色设为红色。将标签高度设置为 100。
现在设置每个 UILabel 的宽度:
令人惊讶的是,从
UILabel
到滚动视图,而不是堆栈视图,并选择相等的宽度。
重复:
不要控制从 UILabel 拖动到 UILabel 的父级 - 转到祖父级。(换句话说,一直到滚动视图,不要到堆栈视图。)
就是这么简单。这就是秘密。
秘密提示 - 苹果错误:
它不适用于仅一项!添加一些标签以使演示工作。
你完成了。
提示:您必须为每个新项目添加高度。任何滚动堆栈视图中的每个项目都必须具有固有大小(例如标签)或添加显式高度约束。
替代方法:
回顾一下上述方法:令人惊讶的是,将标签的宽度设置为滚动视图(而不是堆栈视图)的宽度。
这是另一种方法...
从堆栈视图拖动到滚动视图,并添加“宽度相等”约束。这看起来很奇怪,因为您已经将左右固定,但您就是这样做的。不管它看起来多么奇怪,这就是秘密。
所以你有两个选择:
- 令人惊讶的是,将堆栈视图中每个项目的宽度设置为滚动视图祖父母(而不是堆栈视图父)的宽度。
或者
- 令人惊讶的是,将堆栈视图的“宽度相等”设置为滚动视图 -即使您确实将堆栈视图的左右边缘固定到滚动视图。
需要明确的是,使用其中一种方法,不要同时使用两种方法。
正如 Eik 所说,UIStackView
一起UIScrollView
玩得很好,看这里。
关键是UIStackView
处理不同内容的可变高度/宽度,UIScrollView
然后在滚动/弹跳该内容方面做得很好:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
我想做同样的事情,偶然发现了这篇优秀的帖子。如果您想使用锚 API 以编程方式执行此操作,这就是要走的路。
总而言之,将你的嵌入你UIStackView
的UIScrollView
,并设置锚约束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
水平滚动(UIScrollView 中的 UIStackView)
用于水平滚动。首先,创建 aUIStackView
和 aUIScrollView
并将它们添加到您的视图中,方法如下:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
记住在和 上设置translatesAutoresizingMaskIntoConstraints
to :false
UIStackView
UIScrollView
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
必须等于或大于UIScrollView
anchor的宽度:
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
只需将其添加到viewdidload
:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
您可以尝试ScrollableStackView:https ://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];
// ...
为 macOS Catalyst 添加一些新视角。由于 macOS 应用程序支持窗口大小调整,因此您可能UIStackView
会从不可滚动状态转换为可滚动状态,反之亦然。这里有两件微妙的事情:
UIStackView
旨在适应所有区域。- 在过渡期间,
UIScrollView
将尝试调整其边界以考虑导航栏(或 macOS 应用程序中的工具栏)下方新获得/丢失的区域。
不幸的是,这将创建一个无限循环。我对UIScrollView
和 它不是很熟悉adjustedContentInset
,但是从我登录它的layoutSubviews
方法来看,我看到了以下行为:
- 一放大窗户。
UIScrollView
尝试缩小其边界(因为不需要工具栏下方的区域)。UIStackView
跟随。- 不知何故
UIScrollView
不满意,决定恢复到更大的界限。这让我感觉很奇怪,因为我从日志中看到的是UIScrollView.bounds.height == UIStackView.bounds.height
. UIStackView
跟随。- 然后循环到第 2 步。
在我看来,两个步骤可以解决这个问题:
- 对齐
UIStackView.top
到UIScrollView.topMargin
。 - 设置
contentInsetAdjustmentBehavior
为.never
。
在这里,我关心的是一个垂直可滚动的视图,它具有垂直增长的UIStackView
. 对于水平对,相应地更改代码。
希望它对将来的任何人有所帮助。在互联网上找不到任何人提到这一点,我花了很长时间才弄清楚发生了什么。
如果有人在寻找水平滚动视图
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()
}
如果您有约束将 Stack View 在滚动视图内垂直居中,只需将其删除。
首先设计你的视图,最好是像 Sketch 这样的东西,或者了解你想要什么作为可滚动内容。
在此之后使视图控制器自由形式(从属性检查器中选择)并根据视图的内在内容大小设置高度和宽度(从尺寸检查器中选择)。
之后在视图控制器中放置一个滚动视图,这是一个逻辑,我发现它在 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)
]
只需确保定义了每个子视图的高度!
在场景上放置一个滚动视图,并调整其大小以填充场景。然后,在滚动视图中放置一个堆栈视图,并将添加项目按钮放置在堆栈视图中。一切就绪后,设置以下约束:
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
是关键。
在我的情况下,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
创建了在垂直和水平轴滚动上工作的示例项目。
https://gitlab.com/muhasturk/bmh.example.stackinsidescroll.ios
将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
}
使用 Storyboard 执行此操作时,每个人似乎都错过了修复乘数!当您按照任何人教程中的上述步骤并将常量重置为 0 时,还会检查乘数并将其重置为 1,当视觉链接保持在原位时,它将采取一些其他因素
我发现我可以简单地在 UIStackView 中制作 LOOOOOOOOOOOONG 文本块壁球或拉伸
只需将约束添加到滚动视图左上右下,缺点 0
将约束添加到指向滚动条的堆栈视图内容布局指南
然后从 Frame Layout Guide 添加等宽或等高约束。Pick :如果内容需要垂直滚动,则为宽度,如果需要水平滚动,则为高度。
现在这里是关键。编辑每个约束并将常量重置为 0 并将乘数设置回 1!!!!!!!
如果你不这样做,它会变得很奇怪
如果有效,您可以单击内部内容并鼠标滚动