SnapKit:如何以编程方式为 TableView 单元中的项目设置布局约束

Posted

技术标签:

【中文标题】SnapKit:如何以编程方式为 TableView 单元中的项目设置布局约束【英文标题】:SnapKit: How to set layout constraints for items in a TableViewCell programatically 【发布时间】:2019-02-24 12:16:28 【问题描述】:

我是 swift/ios 开发的初学者。来自 web 开发,与 DOM/Box 模型相比,布局模型让我完全困惑。我知道这意味着只是了解所有事情,但是对于我的生活,我似乎无法弄清楚,我希望像这样的基本示例可以帮助说明一些事情,即使我使用的是 DSL喜欢 snapkit:http://snapkit.io/

我怎样才能为如下布局构建约束:

到目前为止,我所拥有的,显然是错误的如下:

label1.snp.makeConstraints  (make) -> Void in
  make.leading.equalTo(Style.MARGIN)
  make.trailing.equalTo(-Style.MARGIN)
  make.centerX.equalTo(self)
  make.top.equalTo(Style.MARGIN)


label2.snp.makeConstraints  (make) -> Void in
  make.leading.equalTo(Style.MARGIN)
  make.trailing.equalTo(-Style.MARGIN)
  make.centerX.equalTo(self)
  make.top.equalTo(label1.snp.bottom)


exampleImage.snp.makeConstraints  (make) -> Void in
  make.leading.equalTo(0)
  make.trailing.equalTo(0)
  make.top.equalTo(label2.snp.bottom).offset(Style.MARGIN)
  make.bottom.equalTo(0)

其中Style.MARGIN 只是一个设置为20 的常量

我觉得我只需要看一个这样的例子来了解布局是如何流动和构建的,并且也许可以避免像使用网站那样构建它。 我认为在最基本的层面上,对我来说最令人困惑的事情是了解如何将不同动态高度的对象放置在前一个对象的下方,并让 tableViewCell 也相应地调整大小。

【问题讨论】:

你能显示你当前代码的结果吗? exampleImage 是什么? 我建议你看看这个库,它是用锚实现自动布局的更简单和更简洁的方法。 github.com/alexliubj/EZAnchor 【参考方案1】:

从 iOS 9 开始,许多像这样的简单布局都可以使用 UIStackViews 构建,它们是管理子视图及其布局约束的容器视图,因此您不必(就像怀旧评论家一样)。

UIStackViews 对他们有很多好处,除了概念上很简单。它们性能良好,易于使用,并且无需手动操作和更新大量约束即可轻松隐藏/显示视图。

在这种情况下,您有两个 UILabel 和一个 UIImageView 以一定间距堆叠在一起。如果我要实现这个布局,我会将两个标签组合在一个 UIStackView 中,将其添加到带有一些插图的 UIView 中,然后将该视图与 UIImageView 一起添加到另一个 UIStackView 中,如下所示:

UIStackView(1)

UIView(2)

UIStackView (3)

UILabel (4)

UILabel (5)

UIImageView(6)

在代码中:

let containerStackView = UIStackView() // (1)
containerStackView.axis = .vertical

let greenLabel = UILabel() // (4)
greenLabel.text = "Hello,"

let blueLabel = UILabel() // (5)
blueLabel.text = "World!"

let textStackView = UIStackView() // (3)
textStackView.axis = .vertical
textStackView.spacing = 10
textStackView.addArrangedSubview(greenLabel)
textStackView.addArrangedSubview(blueLabel)

let textContainerView = UIView() // (2)
textContainerView.addSubview(textStackView)
textStackView.snp.makeConstraints  make in
    make.edges.equalToSuperview().inset(20)


let imageView = UIImageView(image: UIImage(named: "my-image") // (6)
containerStackView.addArrangedSubview(textContainerView)
containerStackView.addArrangedSubview(imageView)

然后您需要将容器堆栈视图约束到您的 UITableViewCell 的内容视图:

contentView.addSubview(containerStackView)
containerStackView.snp.makeConstraints  make in
    make.edges.equalToSuperview()

我会这样做。当然,您自己管理约束的方法同样有效,可以按如下方式完成:

contentView.addSubview(label1)
label1.snp.makeConstraints  make in
    make.leading.equalToSuperview().offset(20)
    make.trailing.equalToSuperview().offset(-20)
    make.top.equalToSuperview().offset(20)


contentView.addSubview(label2)
label2.snp.makeConstraints  make in
    make.leading.equalToSuperview().offset(20)
    make.trailing.equalToSuperview().offset(-20)
    make.top.equalTo(label1.snp.bottom).offset(10)


contentView.addSubview(exampleImage)
exampleImage.snp.makeConstraints  make in
    make.leading.trailing.bottom.equalToSuperview()
    make.top.equalTo(label2.snp.bottom).offset(20)

请注意,我是在文本编辑器中编写的,因此可能会有一些拼写错误,但总体思路应该成立。

【讨论】:

我明白了,为了一个基本的视图而嵌套这么多东西真的很正常吗?我对此很陌生,但它似乎有很多嵌套。由于图像大小不同,高度将是动态的,例如 instagram 提要。 是的。苹果自己推荐它。您可以查看 Apple 提供的此布局指南:developer.apple.com/library/archive/documentation/… 您好,非常感谢您的详细回答,抱歉耽搁了。我只是想知道,为什么不能用一个同时包含标签和图像的堆栈视图来实现相同的效果? 因为标签在您的设计中每边以固定数量的 20 pt 插入。除了堆栈视图之外,您还需要一些额外的配置来支持它,例如我建议的解决方案或其他限制。 好的,感谢您的帮助!这与更多的研究似乎表明 stackviews 确实是要走的路。【参考方案2】:

以下是您在添加约束时必须直接或间接指定的内容列表:

X 位置 Y 位置 宽度 高度

在进行约束时考虑每一件事可以让这变得非常简单。

首先,label1

    X 位置:我们希望它水平居中 Y 位置:距其超级视图顶部 20 处 宽度:某个数字,它的左侧距离超级视图的左侧 20,右侧距离超级视图的右侧 20,这也意味着 X 位置 高度:您尚未在屏幕截图中指定此高度。我假设你希望它的高度是超级视图的 1/3

这转化为以下约束:

make.topMargin.equalTo(20)
make.leftMargin.equalTo(20)
make.rightMargin.equalTo(-20)
make.height.equalToSuperview().dividedBy(3)

现在label2

    X 位置:我们希望它水平居中 Y 位置:距离label1 底部 10 宽度:某个数字,它的左侧距离超级视图的左侧 20,右侧距离超级视图的右侧 20,这也意味着它的 X 位置 高度:某个数字,使其距离超级视图底部 20 度

这转化为:

make.top.equalTo(label1.snp.bottom).offset(10)
make.leftMargin.equalTo(20)
make.rightMargin.equalTo(-20)
make.bottomMargin.equalTo(-20)

您也可以使用UIStackView 来实现此目的。

关于如何让表格视图根据内容调整其单元格高度,请参阅this。

【讨论】:

以上是关于SnapKit:如何以编程方式为 TableView 单元中的项目设置布局约束的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自动调整其高度的 SnapKit 以编程方式创建 UITableViewCell?

Swift SnapKit 动态高度问题

使用自动布局和 SnapKit 进行相对定位

以编程方式创建 UIView 在 Xcode 11.1 中不起作用

如何在 SnapKit 中设置 heightAnchor 的乘数 - swift

如何获取 snapkit 为 UIView 声明的高度约束?