I have a table view and I using a custom cell which has 3 UI elements as subviews. I have made my UIelements which are labels to shrink as per there content size. Now my problem is to set cell to shrink as per its UIElements and relatively adjust tableview width.
CodePudding user response:
One way could be to:
- create a separate view e.g. named
ThreeElementView
which will be added to the content view of the cell - with all rows available you could call
systemLayoutSizeFitting
to get the maximum width - add an width constraint (
NSLayoutConstraint
) to the table view - if the data of the table view changes, adjust the constraint
The widthContraint
can be setup like this:
private var widthContraint: NSLayoutConstraint?
widthContraint = tableView.widthAnchor.constraint(equalToConstant: 128)
widthContraint?.isActive = true
if let width = calcWidth() {
widthContraint?.constant = width
}
You would also call the last 3 lines before updating the table view with tableView.reloadData()
.
Assuming that data
contains the actual table data, the width calculation could look like this:
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()
}
So for each line you would calculate the width of the contents and finally return the maximum value.
Self-Contained Test
Here is a self-contained test of the above. The UI has been built programmatically in code so that the result is easier to follow. If you press the button, you can see that the width of the tableview then also dynamically adjusts just by setting the constraint.
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()
}
}
Demo