【问题标题】:UIStackView distribution and alignment of a multiline UILabel多行UILabel的UIStackView分布和对齐
【发布时间】:2018-08-31 00:01:26
【问题描述】:

我正在努力解决一些基本的 UIStackView 分布和对齐问题。

我有一个 UICollectionViewCell,它在 contentView 子视图上有一个水平 UIStackView。这个 UIStackView 对于三个标签本身有一个垂直的 UIStackView,当然还有 UIImageView。

这是下面截图的代码sn-p:

    func createSubViews() {

    // contains the UIStackview with the 3 labels and the UIImageView
    containerStackView = UIStackView()
    containerStackView.axis = .horizontal
    containerStackView.distribution = .fill
    containerStackView.alignment = .top
    contentView.addSubview(containerStackView)

    // the UIStackView for the labels
    verticalStackView = UIStackView()
    verticalStackView.axis = .vertical
    verticalStackView.distribution = .fill
    verticalStackView.spacing = 10.0
    containerStackView.addArrangedSubview(verticalStackView)

    categoryLabel = UILabel()
    categoryLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
    categoryLabel.textColor = UIColor.lightGray
    verticalStackView.addArrangedSubview(categoryLabel)

    titleLabel = UILabel()
    titleLabel.numberOfLines = 3
    titleLabel.lineBreakMode = .byWordWrapping
    verticalStackView.addArrangedSubview(titleLabel)

    timeLabel = UILabel()
    timeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
    timeLabel.textColor = UIColor.lightGray
    verticalStackView.addArrangedSubview(timeLabel)

    // UIImageView
    imageView = UIImageView()
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
    imageView.layer.cornerRadius = 5
    layer.masksToBounds = true
    containerStackView.addArrangedSubview(imageView)
}

我想要实现的是,“时间标签”(“3 天前”)始终放置在每个 UICollectionViewCell 的底部(与 UIImageView 的底部对齐),无论标题标签行如何。

我玩过各种 UIStackView 分布,限制“时间标签”和“标题标签”的拥抱优先级。

但无论如何我都做不好。有什么提示吗?

【问题讨论】:

  • 这听起来不像是 UIStackView 的好用例。我建议完全消除堆栈视图。到那时,配置你想要的布局就变得很简单了。
  • 另外,很难知道你在做什么关于堆栈视图本身的约束。我没有看到任何迹象表明您正在使垂直堆栈视图成为单元格的高度。那么为什么底部标签会在单元格的底部呢?

标签: ios ios11 uistackview


【解决方案1】:

更新

由于您要设置titleLabel.numberOfLines = 3,因此一种方法就是在标题文本中添加三个换行符。这将强制 titleLabel 始终使用其三行的全部高度,从而将 timeLabel 强制到底部。

也就是说,当你设置titleLabel.text时,这样做:

titleLabel.text = theTitle + "\n\n\n"

原创

如果您让其中一个标签垂直拉伸,则拉伸标签的文本将在拉伸标签的边界内垂直居中,这不是您想要的。所以我们不能让标签垂直拉伸。因此我们需要引入一个可以拉伸但不可见的内边距视图。

如果填充视图被压缩到零高度,堆栈视图仍会在其前后放置间距,从而导致 titleLabeltimeLabel 之间出现双倍间距,这也是您不希望的。

所以我们需要使用填充视图来实现所有间距。将verticalStackView.spacing 更改为 0。

categoryLabel 之后、titleLabel 之前添加一个名为padding1 的通用UIViewverticalStackView。将其高度限制为等于 10。

titleLabel 之后、timeLabel 之前添加一个名为padding2 的通用UIViewverticalStackView。将其高度限制为大于或等于 10,以便它可以拉伸。

categoryLabeltitleLabeltimeLabel的垂直拥抱优先级设置为必需,使其不会垂直拉伸。

verticalStackView 的高度限制为containerStackView 的高度,以便在需要时拉伸其排列的一个或多个子视图以填充可用的垂直空间。唯一可以拉伸的排列好的子视图是padding2,所以它会拉伸,保持标题文本靠近顶部,时间文本在底部。

另外,将containerStackView 限制在contentView 的范围内并设置containerStackView.translatesAutoresizingMaskIntoConstraints = false

结果:

这是我的游乐场:

import UIKit
import PlaygroundSupport

class MyCell: UICollectionViewCell {

    var containerStackView: UIStackView!
    var verticalStackView: UIStackView!
    var categoryLabel: UILabel!
    var titleLabel: UILabel!
    var timeLabel: UILabel!
    var imageView: UIImageView!

    func createSubViews() {

        // contains the UIStackview with the 3 labels and the UIImageView
        containerStackView = UIStackView()
        containerStackView.axis = .horizontal
        containerStackView.distribution = .fill
        containerStackView.alignment = .top
        contentView.addSubview(containerStackView)

        // the UIStackView for the labels
        verticalStackView = UIStackView()
        verticalStackView.axis = .vertical
        verticalStackView.distribution = .fill
        verticalStackView.spacing = 0
        containerStackView.addArrangedSubview(verticalStackView)

        categoryLabel = UILabel()
        categoryLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
        categoryLabel.textColor = UIColor.lightGray
        verticalStackView.addArrangedSubview(categoryLabel)

        let padding1 = UIView()
        verticalStackView.addArrangedSubview(padding1)

        titleLabel = UILabel()
        titleLabel.numberOfLines = 3
        titleLabel.lineBreakMode = .byWordWrapping
        verticalStackView.addArrangedSubview(titleLabel)

        let padding2 = UIView()
        verticalStackView.addArrangedSubview(padding2)

        timeLabel = UILabel()
        timeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
        timeLabel.textColor = UIColor.lightGray
        verticalStackView.addArrangedSubview(timeLabel)

        // UIImageView
        imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        imageView.layer.cornerRadius = 5
        layer.masksToBounds = true
        containerStackView.addArrangedSubview(imageView)

        categoryLabel.setContentHuggingPriority(.required, for: .vertical)
        titleLabel.setContentHuggingPriority(.required, for: .vertical)
        timeLabel.setContentHuggingPriority(.required, for: .vertical)
        imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        containerStackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: containerStackView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor),
            contentView.topAnchor.constraint(equalTo: containerStackView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor),
            verticalStackView.heightAnchor.constraint(equalTo: containerStackView.heightAnchor),
            padding1.heightAnchor.constraint(equalToConstant: 10),
            padding2.heightAnchor.constraint(greaterThanOrEqualToConstant: 10),
            ])
    }
}

let cell = MyCell(frame: CGRect(x: 0, y: 0, width: 320, height: 110))
cell.backgroundColor = .white
cell.createSubViews()
cell.categoryLabel.text = "MY CUSTOM LABEL"
cell.titleLabel.text = "This is my title"
cell.timeLabel.text = "3 days ago"
cell.imageView.image = UIGraphicsImageRenderer(size: CGSize(width: 110, height:110)).image { (context) in
    UIColor.blue.set()
    UIRectFill(.infinite)
}

PlaygroundPage.current.liveView = cell

【讨论】:

  • 我接受这个作为答案,因为使用 UIStackView 和解释...... Tnx。
【解决方案2】:

问题在于垂直堆栈视图。您显然想说:中间标签的顶部应该拥抱 MyCustomLabel 底部,但 3 Days Ago 底部应该拥抱整个底部。这不是你可以对堆栈视图说的话。

即使这不是你想说的,你仍然需要让垂直堆栈视图占据单元格的整个高度,你打算怎么做?在您展示的代码中,您根本不这样做;实际上,基于该代码,您的堆栈视图具有零大小,这将导致各种问题。

因此,我建议您摆脱所有堆栈视图并直接配置布局。您的布局很容易使用自动布局约束进行配置。

【讨论】:

    猜你喜欢
    • 2021-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2013-10-31
    • 2017-05-13
    相关资源
    最近更新 更多