ios - 多行 UILabel 的 UIStackView 分布和对齐

标签 ios ios11 uistackview

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

我有一个 UICollectionViewCell,它在 contentView subview 上有一个水平的 UIStackView。这个 UIStackView 有一个用于三个标签本身的垂直 UIStackView,当然还有 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 = 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)
}

enter image description here

我想要实现的是,“时间标签”(“3 天前”)始终位于每个 UICollectionViewCell 的底部(与 UIImageView 的底部对齐),而不管不同的标题标签行。

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

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

最佳答案

更新

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

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

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

原创

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

如果 padding view 被压缩到零高度,stack view 仍然会在它前后放置间距,导致 titleLabeltimeLabel 之间的双倍间距,你也不想要。

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

categoryLabel 之后,titleLabel 之前,将一个名为 padding1 的通用 UIView 添加到 verticalStackView >。将其高度限制为 10。

titleLabel 之后,timeLabel 之前,将一个名为 padding2 的通用 UIView 添加到 verticalStackView >。将其高度限制为大于或等于 10,以便它可以拉伸(stretch)。

categoryLabeltitleLabeltimeLabel的垂直拥抱优先级设置为required,使其不会垂直拉伸(stretch)。

verticalStackView 的高度限制为 containerStackView 的高度,以便它在需要时拉伸(stretch)一个或多个已排列的 subview 以填充可用的垂直空间。唯一可以拉伸(stretch)的排列 subview 是 padding2,因此它会拉伸(stretch),将标题文本保持在顶部附近,将时间文本保持在底部。

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

结果:

short title

long title

这是我的 Playground :

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

关于ios - 多行 UILabel 的 UIStackView 分布和对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49413584/

相关文章:

ios - 将 componentsseparatedbystring 添加到具有核心数据的谓词中

ios - 展开/折叠 UITableview

ios - 使用 NSDictionary 填充 UITableView,其中包含来自 mysql db 的已解析 JSON 数据

ios - 代码 : Stack View Move to next line if screen is too small

ios - 在动态框架中使用 +load 来设置快速 WKNavigationDelegate 是否安全?

pdf - 如何在iOS11的PDFKit中添加图片标注

ios - 在 iPhone X 上横向使用最大屏幕

ios - 设置 UIBarButtonItem 动画不适用于 iOS 11

IOS Swift 嵌套垂直 StackView

ios - 一些 tableView 行不显示,除非我滚动然后消失