多行 UILabel 的 UIStackView 分布和对齐
UIStackView distribution and alignment of a multiline UILabel
我正在努力解决一些基本的 UIStackView 分布和对齐问题。
我有一个 UICollectionViewCell,它在 contentView 子视图中有一个水平的 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)
}
我想要实现的是,"time label"(“3 天前”)始终位于每个 UICollectionViewCell 的底部(与 UIImageView 的底部对齐),无论标题如何标签线。
我玩过各种 UIStackView 分布,约束 "time label" 和 "title label" 的拥抱优先级。
但无论如何我都做不对。有什么提示吗?
问题出在垂直堆栈视图上。您显然想说:中间标签的顶部应该紧贴 MyCustomLabel 的底部,但 3 天前的底部应该紧贴整个底部。这不是你可以对堆栈视图说的话。
即使这不是您想说的,您仍然需要使垂直堆栈视图占据单元格的整个高度,您将如何做到这一点?在您展示的代码中,您根本不这样做;实际上,基于该代码,您的堆栈视图 大小为零 ,这将导致各种问题。
因此,我建议您去掉所有堆栈视图,直接配置布局。您的布局很容易使用自动布局约束进行配置。
更新
由于您设置的是 titleLabel.numberOfLines = 3
,一种方法是在标题文本中添加三个换行符。这将迫使 titleLabel
始终消耗其三行的完整高度,迫使 timeLabel
到底部。
也就是说,当你设置titleLabel.text
时,这样做:
titleLabel.text = theTitle + "\n\n\n"
原创
如果让其中一个标签垂直拉伸,拉伸标签的文本将在拉伸标签的边界内垂直居中,这不是您想要的。所以我们不能让标签垂直拉伸。因此我们需要引入一个可以拉伸但不可见的填充视图。
如果 padding view 被压缩到零高度,stack view 仍然会在它前后放置间距,导致 titleLabel
和 timeLabel
之间的 double-spacing,你也不想要
所以我们需要使用填充视图来实现所有间距。将 verticalStackView.spacing
更改为 0.
在 categoryLabel
之后、titleLabel
之前,将名为 padding1
的通用 UIView
添加到 verticalStackView
。将其高度限制为 10。
在 titleLabel
之后、timeLabel
之前,将名为 padding2
的通用 UIView
添加到 verticalStackView
。将其高度限制为大于或等于 10,以便它可以拉伸。
将categoryLabel
、titleLabel
和timeLabel
的垂直拥抱优先级设置为必需,这样它们就不会垂直拉伸。
将 verticalStackView
的高度限制为 containerStackView
的高度,以便在需要时拉伸一个或多个其排列的子视图以填充可用的垂直 space。唯一可以拉伸的排列子视图是 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 分布和对齐问题。
我有一个 UICollectionViewCell,它在 contentView 子视图中有一个水平的 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)
}
我想要实现的是,"time label"(“3 天前”)始终位于每个 UICollectionViewCell 的底部(与 UIImageView 的底部对齐),无论标题如何标签线。
我玩过各种 UIStackView 分布,约束 "time label" 和 "title label" 的拥抱优先级。
但无论如何我都做不对。有什么提示吗?
问题出在垂直堆栈视图上。您显然想说:中间标签的顶部应该紧贴 MyCustomLabel 的底部,但 3 天前的底部应该紧贴整个底部。这不是你可以对堆栈视图说的话。
即使这不是您想说的,您仍然需要使垂直堆栈视图占据单元格的整个高度,您将如何做到这一点?在您展示的代码中,您根本不这样做;实际上,基于该代码,您的堆栈视图 大小为零 ,这将导致各种问题。
因此,我建议您去掉所有堆栈视图,直接配置布局。您的布局很容易使用自动布局约束进行配置。
更新
由于您设置的是 titleLabel.numberOfLines = 3
,一种方法是在标题文本中添加三个换行符。这将迫使 titleLabel
始终消耗其三行的完整高度,迫使 timeLabel
到底部。
也就是说,当你设置titleLabel.text
时,这样做:
titleLabel.text = theTitle + "\n\n\n"
原创
如果让其中一个标签垂直拉伸,拉伸标签的文本将在拉伸标签的边界内垂直居中,这不是您想要的。所以我们不能让标签垂直拉伸。因此我们需要引入一个可以拉伸但不可见的填充视图。
如果 padding view 被压缩到零高度,stack view 仍然会在它前后放置间距,导致 titleLabel
和 timeLabel
之间的 double-spacing,你也不想要
所以我们需要使用填充视图来实现所有间距。将 verticalStackView.spacing
更改为 0.
在 categoryLabel
之后、titleLabel
之前,将名为 padding1
的通用 UIView
添加到 verticalStackView
。将其高度限制为 10。
在 titleLabel
之后、timeLabel
之前,将名为 padding2
的通用 UIView
添加到 verticalStackView
。将其高度限制为大于或等于 10,以便它可以拉伸。
将categoryLabel
、titleLabel
和timeLabel
的垂直拥抱优先级设置为必需,这样它们就不会垂直拉伸。
将 verticalStackView
的高度限制为 containerStackView
的高度,以便在需要时拉伸一个或多个其排列的子视图以填充可用的垂直 space。唯一可以拉伸的排列子视图是 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