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

4
我是一名swift/iOS开发的初学者。来自web开发领域,与DOM/Box模型相比,布局模型完全令我困惑。我知道这意味着要理解所有内容,但是我就是不能弄清楚,我希望像这样的基本示例可以帮助说明一些问题,即使我使用类似snapkit的DSL:http://snapkit.io/ 我该如何构建以下布局的约束条件?

diagram

我目前得到的,显然是错误的如下所示:
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是什么? - Sweeper
我建议您查看这个库,它是使用锚点实现自动布局的更简单、更整洁的方式。 https://github.com/alexliubj/EZAnchor - Alex L
2个回答

6

自从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 的展示一样。 - waffl
是的,苹果公司自己也推荐这样做。您可以查看苹果公司的布局指南:https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html - l_priebe
你好,非常感谢你详细的回答,抱歉回复晚了。我只是想知道,为什么不能使用一个包含标签和图像的单个堆栈视图来实现相同的效果呢? - waffl
由于标签在设计中每侧都向内缩进了固定的 20 个点,因此您需要额外的配置来支持它,除了堆栈视图之外,例如我建议的解决方案或其他约束条件。 - l_priebe
好的,感谢您的帮助!这个结论加上更多的研究似乎表明stackviews确实是正确的选择。 - waffl
啊,对不起,我觉得我感到困惑的原因是你示例中的UIImageView(6)似乎嵌套在UIView(2)中,但实际上它是UIStackView(1)的ArrangedSubView,对吧? - waffl

1
以下是必须直接或间接指定的约束条件列表:
  • X位置
  • Y位置
  • 宽度
  • 高度
在进行约束时考虑这些内容可以使其非常简单明了。
首先,label1
  1. X位置:我们希望它水平居中
  2. Y位置:距其父视图顶部20个单位
  3. 宽度:某个数字,使其左侧距离父视图左侧20个单位,右侧距离父视图右侧20个单位,这也意味着X位置
  4. 高度:您在截图中没有指定此项。我将假设您希望其高度为父视图的1/3
这转化为以下约束条件:
make.topMargin.equalTo(20)
make.leftMargin.equalTo(20)
make.rightMargin.equalTo(-20)
make.height.equalToSuperview().dividedBy(3)

现在是关于label2的内容:
  1. X位置:我们希望它水平居中。
  2. Y位置:距离label1底部10个单位。
  3. 宽度:某个数字,使其左侧距离父视图左侧20个单位,右侧距离父视图右侧20个单位,这也意味着它的X位置。
  4. 高度:某个数字,使其距离父视图底部20个单位。

翻译完毕。

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

你也可以使用 UIStackView 来实现这个功能。
关于如何使表格视图根据内容调整单元格高度,请参见 this

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接