I am struggling this issue which is related to UITableViewCell
. I have a subclass UITableViewCell
called ApplicantMessageCell
and it has some subviews, labels, imageviews etc. Top part of it does not depend on the state. Just gets the data, changes labels text and imageView's image.
However for the bottom part I have completely different 3 subclasses of UIView
for each state coming in. I need to show related UIView
subclass at the bottom part of ApplicationMessageCell
. But I could not find a way to do it.
UITableViewCell
subclasses for each state but I didnot want to go that road beacuse this is just one case, I have more.UIView
which will behave like UILabel
when it comes to resizing itself. I could not manage to it.UIView
subclass regarding each state and explicitly showing the one/hiding rest would solve it but I believe there are better ways to achieve this.I did not share any code because I think this more of a theoretical question, but of course I will if anyone requests.
Thanks in advance.
Here is a quick example...
The cell class has two labels, a stack view, and 3 views (red, green, blue) with varying heights to use as the "show or not" views:
Three views of varying heights are then added to the stack view. Presumably, the constraints on the subviews of your different views will determine their respective heights. For this example, they are set to 40, 80 and 160.
Review the comments in the following code - it should be pretty self-explanatory:
class ApplicantMessageCell: UITableViewCell {
let titleLabel = UILabel()
let subLabel = UILabel()
let stackView = UIStackView()
let viewA = UIView()
let viewB = UIView()
let viewC = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
[titleLabel, subLabel, stackView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(v)
}
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain titleLabel at top
titleLabel.topAnchor.constraint(equalTo: g.topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
titleLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// subLabel 8-pts below titleLabel
subLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8.0),
subLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
subLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// stackView 8-pts below subLabel
stackView.topAnchor.constraint(equalTo: subLabel.bottomAnchor, constant: 8.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
])
// constrain stackView bottom to bottom
// this will avoid auto-layout complaints while the cells are configured
let c = stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor)
c.priority = .defaultHigh
c.isActive = true
// UI element properties
stackView.axis = .vertical
stackView.spacing = 8
titleLabel.backgroundColor = .yellow
subLabel.backgroundColor = .cyan
viewA.backgroundColor = .red
viewB.backgroundColor = .green
viewC.backgroundColor = .blue
// you'll be filling the views with something to determine their heights
// but here we'll just set them to 40, 80 and 160 pts
for (v, h) in zip([viewA, viewB, viewC], [40.0, 80.0, 160.0]) {
stackView.addArrangedSubview(v)
v.heightAnchor.constraint(equalToConstant: CGFloat(h)).isActive = true
}
}
func fillData(_ top: String, sub: String, showViews: [Bool]) -> Void {
titleLabel.text = top
subLabel.text = sub
// hide views as defined in showViews array
for (v, b) in zip(stackView.arrangedSubviews, showViews) {
v.isHidden = !b
}
}
}
struct ApplicationStruct {
var title: String = ""
var subTitle: String = ""
var showViews: [Bool] = [true, true, true]
}
class FarukTableViewController: UITableViewController {
var theData: [ApplicationStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<20 {
// cycle through views 1, 2, 3
let b1 = i % 3 == 0
let b2 = i % 3 == 1
let b3 = i % 3 == 2
let a = [b1, b2, b3]
let d = ApplicationStruct(title: "Title \(i)", subTitle: "", showViews: a)
theData.append(d)
}
// just to test, set more than one view visible in a couple cells
theData[11].showViews = [true, false, true] // red and blue
theData[12].showViews = [false, true, true] // green and blue
theData[13].showViews = [true, true, false] // red and green
theData[14].showViews = [true, true, true] // all three
tableView.register(ApplicantMessageCell.self, forCellReuseIdentifier: "cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ApplicantMessageCell
let d = theData[indexPath.row]
let subStr = "showViews: " + d.showViews.description
c.fillData(d.title, sub: subStr, showViews: d.showViews)
return c
}
}
Result where first row shows "ViewType1" second row shows "ViewType2" and third row shows "ViewType3" ... then the rows cycle, until we hit row "Title 11" where we've set a few rows to show more than one of the "subview types":