如何修复UIStackView约束间距问题?嵌入后布局混乱
解决水平StackView嵌入垂直StackView后的布局错乱问题
你的布局混乱主要是因为水平StackView的distribution设置不合理,以及部分StackView的对齐方式和层级设计有问题,以下是具体的修复方案:
核心问题分析
- 你给
reputationStackView和reviewStackView设置了equalCentering的distribution,这会强制让子视图在水平方向上均匀分布,当StackView宽度足够时,会出现大量不合理的空隙。 - 垂直StackView的alignment设为
.top,导致内部的水平StackView没有充满父视图宽度,进一步加剧了间距错乱。 dateStackView只包含一个label,使用equalSpacing的distribution完全没有必要,反而会干扰布局。
修复步骤
- 调整水平StackView的distribution:将
reputationStackView、reviewStackView的distribution改为.fill(或.fillProportionally,根据需求选择),让子视图按自身大小自然排列。 - 统一对齐方式:垂直StackView的alignment改为
.fill,确保内部水平StackView充满父视图宽度;水平StackView的alignment改为.center,让子视图垂直居中,视觉更整齐。 - 简化dateStackView:直接将
dateLabel添加到垂直StackView中,无需额外嵌套StackView。 - 给标签添加优先级:为避免文字被截断,给所有label设置合适的内容压缩抵抗优先级。
修改后的完整代码
import UIKit import SnapKit import PlaygroundSupport extension UIStackView { func removeAllSubviews() { arrangedSubviews.forEach { $0.removeFromSuperview() } } convenience init(frame: CGRect = .zero, alignment: UIStackView.Alignment = .fill, axis: NSLayoutConstraint.Axis = .vertical, distribution: UIStackView.Distribution = .equalSpacing, spacing: CGFloat = 0.0) { self.init(frame: frame) self.alignment = alignment self.axis = axis self.distribution = distribution self.spacing = spacing } } class UserCell: UIView { private lazy var reputationLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 15, weight: .bold) label.text = "Reputation" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var increasePerformaceLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 10, weight: .semibold) label.text = "Increase Performance" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var increasePerformaceImage : UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.image = UIImage(systemName: "pencil.circle.fill") return imageView }() private lazy var reviewsImage : UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.image = UIImage(systemName: "pencil.circle.fill") return imageView }() private lazy var reviewsLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 13, weight: .bold) label.text = "Reviews" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var dateLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 13, weight: .bold) label.text = "All Time as of 2020-10-10" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var scoreLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 15, weight: .semibold) label.textColor = .darkGray label.text = "325" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var ratingsLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 12, weight: .semibold) label.textColor = .darkGray label.text = "Ratings" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var ratingsNumberLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 12, weight: .semibold) label.textColor = .darkGray label.text = "4.5" label.setContentCompressionResistancePriority(.required, for: .horizontal) return label }() private lazy var ratingStarImage : UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.image = UIImage(systemName: "pencil.circle.fill") return imageView }() override init(frame: CGRect) { super.init(frame: frame) addComponents() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func addComponents() { // 调整distribution为.fill,alignment为.center let reputationStackView = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing:25) let reviewStackView = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 25) let ratingStackView = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 20) // 垂直StackView设置为.fill,让内部水平StackView充满宽度 let verticalStackView = UIStackView(frame: .zero, alignment: .fill, axis: .vertical, distribution: .fill, spacing: 9) reputationStackView.addArrangedSubview(reputationLabel) reputationStackView.addArrangedSubview(increasePerformaceLabel) reputationStackView.addArrangedSubview(increasePerformaceImage) reviewStackView.addArrangedSubview(reviewsImage) reviewStackView.addArrangedSubview(reviewsLabel) reviewStackView.addArrangedSubview(scoreLabel) ratingStackView.addArrangedSubview(ratingsLabel) ratingStackView.addArrangedSubview(ratingsNumberLabel) ratingStackView.addArrangedSubview(ratingStarImage) // 直接添加dateLabel到垂直StackView,无需额外嵌套 verticalStackView.addArrangedSubview(reputationStackView) verticalStackView.addArrangedSubview(reviewStackView) verticalStackView.addArrangedSubview(dateLabel) verticalStackView.addArrangedSubview(ratingStackView) addSubview(verticalStackView) increasePerformaceImage.snp.makeConstraints { (make) in make.width.height.equalTo(20) } reviewsImage.snp.makeConstraints { (make) in make.width.height.equalTo(20) } ratingStarImage.snp.makeConstraints { (make) in make.width.height.equalTo(20) } verticalStackView.snp.makeConstraints { (make) in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)) } } } let userCell = UserCell(frame: CGRect(x: 0, y: 0, width: 400, height: 120)) PlaygroundPage.current.liveView = userCell
额外优化说明
- 给垂直StackView添加了
inset,避免内容紧贴父视图边缘,视觉更舒适。 - 给所有标签设置了
contentCompressionResistancePriority,确保文字不会因为布局挤压被截断。 - 调整了userCell的高度,适配修改后的布局内容。
内容的提问来源于stack exchange,提问作者titanPlanet12
相关产品推荐
相关产品推荐

