具有动态大小内容的 UIScrollView
Posted
技术标签:
【中文标题】具有动态大小内容的 UIScrollView【英文标题】:UIScrollView with dynamically sized content 【发布时间】:2020-05-05 14:59:47 【问题描述】:(Xcode 11,斯威夫特)
作为 ios 和 Autolayout 的新手,我正在努力实现一个相当简单的(恕我直言)视图,该视图显示 [垂直] 项目列表。唯一的问题是项目是动态决定的,每个项目都可以是文本或图像(其中任何一个都可能相当大,因此需要滚动)。 WebView 不是一个选项,所以它必须在本地实现。
我是这样理解这个过程的:
在 IB 中创建一个 UIScrollView 并将其调整为外框的大小。 将容器视图创建为 UIScrollView 的子视图(同样,在 IB 中),并将其大小设置为相同。 设置两个宽度相等的约束 在运行时,使用 UILabels/UIImageViews 填充容器视图,并以编程方式设置约束以确保正确的布局。 “告诉”滚动视图关于子视图的高度,以使其管理其滚动。这是正确的方法吗?它似乎对我不起作用(对于一个将非常高的图像动态添加到容器视图的玩具示例 - 我无法让滚动工作)。在上述过程中执行最后一步的正确方法是什么 - 只需将滚动视图的 contentSize 强制为填充容器视图的大小(它似乎对我不起作用)。任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:在运行时向滚动视图添加多个元素时,您可能会发现使用UIStackView
会更容易...如果设置正确,它会随着每个添加的对象自动增加高度。
举个简单的例子……
1) 首先添加一个UIScrollView
(我给它一个蓝色背景以便于查看)。将其在所有 4 个方面都约束为零:
请注意,我们看到“红色圆圈”表示缺少/冲突的约束。暂时忽略它。
2) 在滚动视图中添加一个UIView
作为“内容视图”(我给它一个systemYellow 背景以便于查看)。在内容布局指南的所有 4 个方面将其限制为零——这将(最终)定义滚动视图的内容大小。还要将其限制为等宽和等高框架布局指南:
重要步骤:选择高度约束,然后在Size Inspector
窗格中选择Placeholder - Remove at build time
复选框。这将在设计时满足 IB 中的自动布局,但将允许该视图的高度根据需要缩小/增长。
3) 将垂直UIStackView
添加到“内容视图”。将其在所有 4 个面上都约束为零。将其属性配置为Fill / Fill / 8
(如下图):
4) 在视图控制器类的堆栈视图中添加一个@IBOutlet
连接。现在,在运行时,当您将 UI 元素添加到堆栈视图时,您的所有“可滚动性”都将由自动布局处理。
这是一个示例类:
class DynaScrollViewController: UIViewController
@IBOutlet var theStackView: UIStackView!
override func viewDidLoad()
super.viewDidLoad()
// local var so we can reuse it
var theLabel = UILabel()
var theImageView = UIImageView()
// create a new label
theLabel = UILabel()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theLabel.translatesAutoresizingMaskIntoConstraints = false
// multi-line
theLabel.numberOfLines = 0
// cyan background to make it easy to see
theLabel.backgroundColor = .cyan
// add 9 lines of text to the label
theLabel.text = (1...9).map( "Line \($0)" ).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .yellow
// add 5 lines of text to the label
theLabel.text = (1...5).map( "Line \($0)" ).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// create a new UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load an image for it - I have one named background
if let img = UIImage(named: "background")
theImageView.image = img
// let's give the image view a 4:3 width:height ratio
theImageView.widthAnchor.constraint(equalTo: theImageView.heightAnchor, multiplier: 4.0/3.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .green
// add 2 lines of text to the label
theLabel.text = (1...2).map( "Line \($0)" ).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load a different image for it - I have one named AquariumBG
if let img = UIImage(named: "AquariumBG")
theImageView.image = img
// let's give this image view a 1:1 width:height ratio
theImageView.heightAnchor.constraint(equalTo: theImageView.widthAnchor, multiplier: 1.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
如果已遵循这些步骤,您应该会得到以下输出:
并且,在滚动到底部之后:
【讨论】:
【参考方案2】:对齐约束(前导/尾随/顶部/底部)
滚动视图和内容视图之间的对齐约束定义了scrollable range of the content
。 例如,
即Scroll View上的(底部)anchor表示外框的(底部)边缘,即Content View的可见部分; Content View 上的(底部)锚点是指实际内容的边缘,如果不滚动到该边缘,它将被隐藏。 与普通用例不同,Scroll View 和 Content View 之间的对齐约束与 Content View 的实际大小无关。它们只影响“内容视图的可滚动范围”,而不影响“实际内容大小”。 Content View 的实际大小必须另外定义。
尺寸限制(宽度/高度)
要实际调整 Content View 的大小,我们可以将 Content View 的大小设置为特定的长度,比如宽/高为 500。如果宽/高超过了 Scroll View 的宽/高,就会有一个滚动条供用户使用滚动。 但是,更常见的情况是,我们希望 Content View 与 Scroll View 具有相同的宽度(或高度)。在这种情况下,我们将有
contentView.width = scrollView.width
Content View 的宽度是指内容的实际全宽。另一方面,Scroll View 的宽度是指 Scroll View 的外部容器框架宽度。当然,不一定要等宽,也可以是a * scrollView.width + b等其他形式。 如果我们的 Content View 比 Scroll View 更高(或更宽),就会出现一个滚动条。 内容视图不仅可以是单个视图,还可以是多个视图,只要它们使用 Scroll View 的对齐和大小约束进行适当的约束即可。
详情可以关注这篇文章:Link。
【讨论】:
以上是关于具有动态大小内容的 UIScrollView的主要内容,如果未能解决你的问题,请参考以下文章
具有动态大小单元格间距问题的 UICollectionViewCell