如何将 UITableView 的宽度设置为等于其中最大单元格的宽度?
How to set width of a UITableView equal to the maximum cells' width inside of it?
我有一个 table 视图,我使用一个自定义单元格,它有 3 个 UI 元素作为子视图。我已经制作了我的 UI 元素,这些元素是根据内容大小缩小的标签。现在我的问题是将单元格设置为根据其 UI 元素缩小并相对调整 table 视图宽度。
一种方法是:
- 创建一个单独的视图,例如名为
ThreeElementView
的内容将被添加到单元格的内容视图中
- 如果所有行都可用,您可以调用
systemLayoutSizeFitting
以获得最大宽度
- 向 table 视图添加宽度约束 (
NSLayoutConstraint
)
- 如果table视图的数据发生变化,调整约束
widthContraint
可以这样设置:
private var widthContraint: NSLayoutConstraint?
widthContraint = tableView.widthAnchor.constraint(equalToConstant: 128)
widthContraint?.isActive = true
if let width = calcWidth() {
widthContraint?.constant = width
}
在使用 tableView.reloadData()
.
更新 table 视图之前,您还可以调用最后 3 行
假设 data
包含实际的 table 数据,宽度计算可能如下所示:
private func calcWidth() -> CGFloat? {
let prototypeView = ThreeElementView()
let widths = data.map { row -> CGFloat in
prototypeView.label1.text = row[0]
prototypeView.label2.text = row[1]
prototypeView.label3.text = row[2]
prototypeView.setNeedsLayout()
prototypeView.layoutIfNeeded()
return prototypeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
}
return widths.max()
}
因此,对于每一行,您将计算内容的宽度,最后 return 最大值。
Self-Contained测试
这是对上述内容的 self-contained 测试。 UI 是在代码中以编程方式构建的,因此结果更容易理解。如果您按下按钮,您可以看到 table 视图的宽度也会通过设置约束动态调整。
ThreeElementView.swift
import UIKit
class ThreeElementView: UIView {
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
init() {
super.init(frame: .zero)
label1.backgroundColor = UIColor(red: 84/255, green: 73/255, blue: 75/255, alpha: 1.0)
label1.textColor = .white
label2.backgroundColor = UIColor(red: 131/255, green: 151/255, blue: 136/255, alpha: 1.0)
label2.textColor = .white
label3.backgroundColor = UIColor(red: 189/255, green: 187/255, blue: 182/255, alpha: 1.0)
label1.translatesAutoresizingMaskIntoConstraints = false
label2.translatesAutoresizingMaskIntoConstraints = false
label3.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label1)
self.addSubview(label2)
self.addSubview(label3)
NSLayoutConstraint.activate([
label1.leadingAnchor.constraint(equalTo: self.leadingAnchor),
label1.topAnchor.constraint(equalTo: self.topAnchor),
label1.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label2.leadingAnchor.constraint(equalTo: label1.trailingAnchor),
label2.topAnchor.constraint(equalTo: self.topAnchor),
label2.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label3.leadingAnchor.constraint(equalTo: label2.trailingAnchor),
label3.topAnchor.constraint(equalTo: self.topAnchor),
label3.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label3.trailingAnchor.constraint(equalTo: self.trailingAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ThreeElementCell.swift
import UIKit
class ThreeElementCell: UITableViewCell {
static let id = "ThreeElementCellId"
let threeElementView = ThreeElementView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
threeElementView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(threeElementView)
NSLayoutConstraint.activate([
threeElementView.topAnchor.constraint(equalTo: contentView.topAnchor),
threeElementView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
threeElementView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
threeElementView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private let tableView = UITableView()
private let addMoreButton = UIButton()
private var data = [
["a", "tiny", "row"],
]
private var widthContraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupButton()
}
@objc func onAddMore() {
if data.count < 2 {
data.append(["a", "little bit", "longer row"])
} else {
data.append(["this is", " finally an even longer", "row"])
}
if let width = calcWidth() {
widthContraint?.constant = width
}
tableView.reloadData()
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: ThreeElementCell.id, for: indexPath) as! ThreeElementCell
let item = data[indexPath.row]
cell.threeElementView.label1.text = item[0]
cell.threeElementView.label2.text = item[1]
cell.threeElementView.label3.text = item[2]
return cell
}
// MARK: - Private
private func setupTableView() {
tableView.backgroundColor = UIColor(red: 245/255, green: 228/255, blue: 215/255, alpha: 1.0)
tableView.register(ThreeElementCell.self, forCellReuseIdentifier: ThreeElementCell.id)
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16.0),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0),
])
widthContraint = tableView.widthAnchor.constraint(equalToConstant: 128)
widthContraint?.isActive = true
if let width = calcWidth() {
widthContraint?.constant = width
}
tableView.delegate = self
tableView.dataSource = self
}
private func setupButton() {
addMoreButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(addMoreButton)
NSLayoutConstraint.activate([
addMoreButton.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 32.0),
addMoreButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
addMoreButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -32.0),
])
addMoreButton.setTitle("Add More Rows", for: .normal)
addMoreButton.setTitleColor(.blue, for: .normal)
addMoreButton.addTarget(self, action: #selector(onAddMore), for: .touchUpInside)
}
private func calcWidth() -> CGFloat? {
let prototypeView = ThreeElementView()
let widths = data.map { row -> CGFloat in
prototypeView.label1.text = row[0]
prototypeView.label2.text = row[1]
prototypeView.label3.text = row[2]
prototypeView.setNeedsLayout()
prototypeView.layoutIfNeeded()
return prototypeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
}
return widths.max()
}
}
演示
我有一个 table 视图,我使用一个自定义单元格,它有 3 个 UI 元素作为子视图。我已经制作了我的 UI 元素,这些元素是根据内容大小缩小的标签。现在我的问题是将单元格设置为根据其 UI 元素缩小并相对调整 table 视图宽度。
一种方法是:
- 创建一个单独的视图,例如名为
ThreeElementView
的内容将被添加到单元格的内容视图中 - 如果所有行都可用,您可以调用
systemLayoutSizeFitting
以获得最大宽度 - 向 table 视图添加宽度约束 (
NSLayoutConstraint
) - 如果table视图的数据发生变化,调整约束
widthContraint
可以这样设置:
private var widthContraint: NSLayoutConstraint?
widthContraint = tableView.widthAnchor.constraint(equalToConstant: 128)
widthContraint?.isActive = true
if let width = calcWidth() {
widthContraint?.constant = width
}
在使用 tableView.reloadData()
.
假设 data
包含实际的 table 数据,宽度计算可能如下所示:
private func calcWidth() -> CGFloat? {
let prototypeView = ThreeElementView()
let widths = data.map { row -> CGFloat in
prototypeView.label1.text = row[0]
prototypeView.label2.text = row[1]
prototypeView.label3.text = row[2]
prototypeView.setNeedsLayout()
prototypeView.layoutIfNeeded()
return prototypeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
}
return widths.max()
}
因此,对于每一行,您将计算内容的宽度,最后 return 最大值。
Self-Contained测试
这是对上述内容的 self-contained 测试。 UI 是在代码中以编程方式构建的,因此结果更容易理解。如果您按下按钮,您可以看到 table 视图的宽度也会通过设置约束动态调整。
ThreeElementView.swift
import UIKit
class ThreeElementView: UIView {
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
init() {
super.init(frame: .zero)
label1.backgroundColor = UIColor(red: 84/255, green: 73/255, blue: 75/255, alpha: 1.0)
label1.textColor = .white
label2.backgroundColor = UIColor(red: 131/255, green: 151/255, blue: 136/255, alpha: 1.0)
label2.textColor = .white
label3.backgroundColor = UIColor(red: 189/255, green: 187/255, blue: 182/255, alpha: 1.0)
label1.translatesAutoresizingMaskIntoConstraints = false
label2.translatesAutoresizingMaskIntoConstraints = false
label3.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label1)
self.addSubview(label2)
self.addSubview(label3)
NSLayoutConstraint.activate([
label1.leadingAnchor.constraint(equalTo: self.leadingAnchor),
label1.topAnchor.constraint(equalTo: self.topAnchor),
label1.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label2.leadingAnchor.constraint(equalTo: label1.trailingAnchor),
label2.topAnchor.constraint(equalTo: self.topAnchor),
label2.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label3.leadingAnchor.constraint(equalTo: label2.trailingAnchor),
label3.topAnchor.constraint(equalTo: self.topAnchor),
label3.bottomAnchor.constraint(equalTo: self.bottomAnchor),
label3.trailingAnchor.constraint(equalTo: self.trailingAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ThreeElementCell.swift
import UIKit
class ThreeElementCell: UITableViewCell {
static let id = "ThreeElementCellId"
let threeElementView = ThreeElementView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
threeElementView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(threeElementView)
NSLayoutConstraint.activate([
threeElementView.topAnchor.constraint(equalTo: contentView.topAnchor),
threeElementView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
threeElementView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
threeElementView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private let tableView = UITableView()
private let addMoreButton = UIButton()
private var data = [
["a", "tiny", "row"],
]
private var widthContraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupButton()
}
@objc func onAddMore() {
if data.count < 2 {
data.append(["a", "little bit", "longer row"])
} else {
data.append(["this is", " finally an even longer", "row"])
}
if let width = calcWidth() {
widthContraint?.constant = width
}
tableView.reloadData()
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: ThreeElementCell.id, for: indexPath) as! ThreeElementCell
let item = data[indexPath.row]
cell.threeElementView.label1.text = item[0]
cell.threeElementView.label2.text = item[1]
cell.threeElementView.label3.text = item[2]
return cell
}
// MARK: - Private
private func setupTableView() {
tableView.backgroundColor = UIColor(red: 245/255, green: 228/255, blue: 215/255, alpha: 1.0)
tableView.register(ThreeElementCell.self, forCellReuseIdentifier: ThreeElementCell.id)
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16.0),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0),
])
widthContraint = tableView.widthAnchor.constraint(equalToConstant: 128)
widthContraint?.isActive = true
if let width = calcWidth() {
widthContraint?.constant = width
}
tableView.delegate = self
tableView.dataSource = self
}
private func setupButton() {
addMoreButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(addMoreButton)
NSLayoutConstraint.activate([
addMoreButton.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 32.0),
addMoreButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
addMoreButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -32.0),
])
addMoreButton.setTitle("Add More Rows", for: .normal)
addMoreButton.setTitleColor(.blue, for: .normal)
addMoreButton.addTarget(self, action: #selector(onAddMore), for: .touchUpInside)
}
private func calcWidth() -> CGFloat? {
let prototypeView = ThreeElementView()
let widths = data.map { row -> CGFloat in
prototypeView.label1.text = row[0]
prototypeView.label2.text = row[1]
prototypeView.label3.text = row[2]
prototypeView.setNeedsLayout()
prototypeView.layoutIfNeeded()
return prototypeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
}
return widths.max()
}
}
演示