防止 UIStackView 压缩 UITableView
Posted
技术标签:
【中文标题】防止 UIStackView 压缩 UITableView【英文标题】:Prevent UIStackView from compressing UITableView 【发布时间】:2019-06-22 14:10:24 【问题描述】:我正在将 UITableView 添加到垂直 UIStackView 中。那个垂直的 UIStackView 在 UIScrollView 中。
但是,除非我对它强制执行明确的高度约束,否则表格不会显示,这显然是我不想做的。
根据这个 SO question and answer UITableView not shown inside UIStackView 这是因为“stackview 尝试尽可能多地压缩内容”
如果我将 UILabel 添加到 StackView,它会显示得很好。 UITableView 有一些特定的东西,这意味着它不是。我正在使用 Xamarin 并在代码中创建 UITableView
this.recentlyOpenedPatientsTable = new UITableView()
RowHeight = UITableView.AutomaticDimension,
EstimatedRowHeight = 44.0f,
AllowsMultipleSelectionDuringEditing = false,
TranslatesAutoresizingMaskIntoConstraints = false,
Editing = false,
BackgroundColor = UIColor.Clear,
TableFooterView = new UIView(),
ScrollEnabled = false,
;
UIScrollView 固定在视图的顶部、底部、左侧和右侧,并且工作正常。它需要我期望的高度。
我已经尝试了这个 SO question 中的两个建议,但都没有奏效。我觉得很奇怪,我找不到其他人有这个问题。
还有其他建议吗?
【问题讨论】:
使用自定尺寸UITableView
我在这里解释过:***.com/a/56406813/5623035
标准UITableView
没有固有高度...您定义它的高度,它会在该高度内滚动其行。如果你想要一个以UIStackView
排列的表格视图,你必须给它一个高度约束,或者使它不可滚动并使用扩展或子类来给它 固有高度。这是一个简单的例子:***.com/a/56703886/6257435
通常我在 UITableViewCell 中依赖 RowHeight = UITableView.AutomaticDimension 和 AutoLayout 的组合。这就是我在这种情况下尝试的方法,但它不起作用
【参考方案1】:
这是一个非常基本的示例,使用 UITableView
子类使其根据内容自动调整高度。
红色按钮(在水平堆栈视图中)是垂直堆栈视图中的第一个排列的子视图。
接下来是表格(单元格的 contentView 为绿色背景,多行标签为黄色背景)。
而最后排列的subView是青色背景UILabel
:
请注意,垂直堆栈视图被限制在距顶部 40 点、前导和尾随,并且至少距底部 40 点。如果您在表格中添加的行数超过了可用高度,则必须滚动才能看到其他行。
//
// TableInStackViewController.swift
//
// Created by Don Mag on 6/24/19.
//
import UIKit
final class ContentSizedTableView: UITableView
override var contentSize:CGSize
didSet
invalidateIntrinsicContentSize()
override var intrinsicContentSize: CGSize
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
class TableInStackCell: UITableViewCell
let theLabel: UILabel =
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .yellow
v.textAlignment = .left
v.numberOfLines = 0
return v
()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .green
contentView.addSubview(theLabel)
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor, constant: 0.0),
theLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: 0.0),
theLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor, constant: 0.0),
theLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor, constant: 0.0),
])
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
class TableInStackViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
let theStackView: UIStackView =
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.alignment = .fill
v.distribution = .fill
v.spacing = 8
return v
()
let addButton: UIButton =
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Add a Row", for: .normal)
v.backgroundColor = .red
return v
()
let deleteButton: UIButton =
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Delete a Row", for: .normal)
v.backgroundColor = .red
return v
()
let buttonsStack: UIStackView =
let v = UIStackView()
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fillEqually
v.spacing = 20
return v
()
let theTable: ContentSizedTableView =
let v = ContentSizedTableView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
()
let bottomLabel: UILabel =
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
v.textAlignment = .center
v.numberOfLines = 0
v.text = "This label is the last element in the stack view."
// prevent label from being compressed when the table gets too tall
v.setContentCompressionResistancePriority(.required, for: .vertical)
return v
()
var theTableData: [String] = [
"Content Sized Table View",
"This row shows that the cell heights will auto-size, based on the cell content (multi-line label in this case).",
"Here is the 3rd default row",
]
var minRows = 1
let reuseID = "TableInStackCell"
override func viewDidLoad()
super.viewDidLoad()
minRows = theTableData.count
view.addSubview(theStackView)
NSLayoutConstraint.activate([
// constrain stack view 40-pts from top, leading and trailing
theStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
theStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
theStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
// constrain stack view *at least* 40-pts from bottom
theStackView.bottomAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -40.0),
])
buttonsStack.addArrangedSubview(addButton)
buttonsStack.addArrangedSubview(deleteButton)
theStackView.addArrangedSubview(buttonsStack)
theStackView.addArrangedSubview(theTable)
theStackView.addArrangedSubview(bottomLabel)
theTable.delegate = self
theTable.dataSource = self
theTable.register(TableInStackCell.self, forCellReuseIdentifier: reuseID)
addButton.addTarget(self, action: #selector(addRow), for: .touchUpInside)
deleteButton.addTarget(self, action: #selector(deleteRow), for: .touchUpInside)
@objc func addRow() -> Void
// add a row to our data source
let n = theTableData.count - minRows
theTableData.append("Added Row: \(n + 1)")
theTable.reloadData()
@objc func deleteRow() -> Void
// delete a row from our data source (keeping the original rows intact)
let n = theTableData.count
if n > minRows
theTableData.remove(at: n - 1)
theTable.reloadData()
func numberOfSections(in tableView: UITableView) -> Int
return 1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return theTableData.count
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as! TableInStackCell
cell.theLabel.text = theTableData[indexPath.row]
return cell
【讨论】:
contentSize.didSet
至少应该在布局无效之前检查该值已更改。调用layoutIfNeeded
不足以正确计算contentSize
。 layoutIfNeeded
将在当前帧中布置单元格,但所有其他单元格的高度将使用估计的高度计算。
我还没有设法让它工作。我的用例有点复杂,StackViews 中有许多 StackView。如果我在 StackView 的底部添加一个标签,该标签会显示。只是那个TableView被压缩了
@PatLong-MunkiiYebee - 那么您需要编辑您的问题并准确解释您要做什么。如果我们不知道,很难提供帮助/解决方案。
已更新。 UIScrollView 很可能是我之前没有提到的关键信息。
@PatLong-MunkiiYebee - 好吧,您添加到问题中的代码表明您正在使用默认的UITableView
,这不会满足您的要求。您需要像我的示例代码一样使用ContentSizedTableView
。以上是关于防止 UIStackView 压缩 UITableView的主要内容,如果未能解决你的问题,请参考以下文章