This question is being updated every time I act on someone's suggestion here. Unfortunately though, non of the minor housekeeping suggestions helped enough, so the problem remains.
In the nutshell:
Right now though, as you can see from the picture, labelOne disappeared altogether and labelTwo has weird layout. Something is wrong with constraints I guess. Please help me fix the constraints right.
import UIKit
class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var secondTableView: UITableView!
var stuff = [("Some other stuff 1"),("Some other stuff 2"),("Some other stuff 3")]
var insertionText: String = ""
override func viewDidLoad() {
super.viewDidLoad()
secondTableView.delegate = self
secondTableView.dataSource = self
let tableHeader = UIView()
secondTableView.tableHeaderView = tableHeader
tableHeader.backgroundColor = .systemOrange
tableHeader.translatesAutoresizingMaskIntoConstraints = false
let size = tableHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
let height = size.height
let width = size.width
tableHeader.frame = CGRectMake(0, 0, width, height)
let labelOne = UILabel(frame: tableHeader.bounds)
labelOne.text = "USER:"
labelOne.numberOfLines = 0
labelOne.lineBreakMode = .byWordWrapping
labelOne.textAlignment = .center
labelOne.translatesAutoresizingMaskIntoConstraints = false
let labelTwo = UILabel(frame: tableHeader.bounds)
labelTwo.translatesAutoresizingMaskIntoConstraints = false
labelTwo.text = "Inherited text here: \(insertionText)"
labelTwo.numberOfLines = 0
labelTwo.lineBreakMode = .byWordWrapping
labelTwo.textAlignment = .center
labelTwo.translatesAutoresizingMaskIntoConstraints = false
tableHeader.addSubview(labelOne)
tableHeader.addSubview(labelTwo)
labelOne.topAnchor.constraint(
equalTo: tableHeader.layoutMarginsGuide.topAnchor,
constant: 11).isActive = true
labelOne.leadingAnchor.constraint(
equalTo: tableHeader.layoutMarginsGuide.leadingAnchor,
constant: 20).isActive = true
labelOne.trailingAnchor.constraint(
equalTo: tableHeader.layoutMarginsGuide.trailingAnchor,
constant: -20).isActive = true
labelTwo.topAnchor.constraint(
equalTo: labelOne.layoutMarginsGuide.bottomAnchor,
constant: 0).isActive = true
labelTwo.leadingAnchor.constraint(
equalTo: tableHeader.leadingAnchor,
constant: 20).isActive = true
labelTwo.trailingAnchor.constraint(
equalTo: tableHeader.trailingAnchor,
constant: -20).isActive = true
labelTwo.bottomAnchor.constraint(
equalTo: tableHeader.bottomAnchor,
constant: -11).isActive = true }
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let tableHeader = secondTableView.tableHeaderView {
let height = tableHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
var headerFrame = tableHeader.frame
if height != headerFrame.size.height {
headerFrame.size.height = height
tableHeader.frame = headerFrame
secondTableView.tableHeaderView = tableHeader } } }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
stuff.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = secondTableView.dequeueReusableCell(withIdentifier: "SecondTVCell", for: indexPath) as! SecondTVCell
cell.someOtherStuffLabel.text = stuff[indexPath.row]
return cell } }
You're not that far off... but couple of things:
The .tableHeaderView
needs .translatesAutoresizingMaskIntoConstraints
to be True, not False
Because you are using a multi-line label, we have to tell auto-layout how wide the view can be when requesting its "fitting" size - so viewDidLayoutSubviews()
becomes:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let tableHeader = secondTableView.tableHeaderView {
// define maximum width for the header view
let fitSize: CGSize = CGSize(width: secondTableView.frame.width, height: .greatestFiniteMagnitude)
// ask UIKit to give us the "fitting" size
let sz: CGSize = tableHeader.systemLayoutSizeFitting(fitSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
let height: CGFloat = sz.height
var headerFrame = tableHeader.frame
if height != headerFrame.size.height {
headerFrame.size.height = height
tableHeader.frame = headerFrame
secondTableView.tableHeaderView = tableHeader
}
}
}
Here's your controller class, with those slight modifications:
class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var secondTableView: UITableView!
var stuff = [("Some other stuff 1"),("Some other stuff 2"),("Some other stuff 3")]
var insertionText: String = "This is some sample insertion text that is currently over writing the first cell. Let's fix that."
override func viewDidLoad() {
super.viewDidLoad()
secondTableView.delegate = self
secondTableView.dataSource = self
let tableHeader = UIView()
secondTableView.tableHeaderView = tableHeader
tableHeader.backgroundColor = .systemOrange
// SHOULD NOT BE SET TO FALSE
//tableHeader.translatesAutoresizingMaskIntoConstraints = false
// let size = tableHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
// let height = size.height
// let width = size.width
// tableHeader.frame = CGRectMake(0, 0, width, height)
let labelOne = UILabel(frame: tableHeader.bounds)
labelOne.text = "USER:"
labelOne.numberOfLines = 0
labelOne.lineBreakMode = .byWordWrapping
labelOne.textAlignment = .center
labelOne.translatesAutoresizingMaskIntoConstraints = false
let labelTwo = UILabel(frame: tableHeader.bounds)
labelTwo.translatesAutoresizingMaskIntoConstraints = false
labelTwo.text = "Inherited text here: \(insertionText)"
labelTwo.numberOfLines = 0
labelTwo.lineBreakMode = .byWordWrapping
labelTwo.textAlignment = .center
labelTwo.translatesAutoresizingMaskIntoConstraints = false
tableHeader.addSubview(labelOne)
tableHeader.addSubview(labelTwo)
// let's make the constraint setup a little more readable
let g = tableHeader.layoutMarginsGuide
NSLayoutConstraint.activate([
labelOne.topAnchor.constraint(equalTo: g.topAnchor, constant: 11.0),
labelOne.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
labelOne.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 0.0),
labelTwo.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
labelTwo.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
labelTwo.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -11.0),
])
// so we can see the label frames
labelOne.backgroundColor = .cyan
labelTwo.backgroundColor = .yellow
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let tableHeader = secondTableView.tableHeaderView {
// define maximum width for the header view
let fitSize: CGSize = CGSize(width: secondTableView.frame.width, height: .greatestFiniteMagnitude)
// ask UIKit to give us the "fitting" size
let sz: CGSize = tableHeader.systemLayoutSizeFitting(fitSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
let height: CGFloat = sz.height
var headerFrame = tableHeader.frame
if height != headerFrame.size.height {
headerFrame.size.height = height
tableHeader.frame = headerFrame
secondTableView.tableHeaderView = tableHeader
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
stuff.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = secondTableView.dequeueReusableCell(withIdentifier: "SecondTVCell", for: indexPath) as! SecondTVCell
cell.someOtherStuffLabel.text = stuff[indexPath.row]
return cell }
}
I find it very helpful during development to give UI elements contrasting background colors... makes it easy to see the framing at run-time. So I set the header view labels to cyan and yellow, and this is the result:
and when the device is rotated:
Note: when you run this, you will see some auto-layout complaints. That's because we're setting constraints on the labels and adding them (and the header view) to the view hierarchy in viewDidLoad()
... but the constraints have conflicts at that point.
We can safely ignore those, but if we want to avoid them we can give the bottom constraint from the second label a priority of less-than required:
let g = tableHeader.layoutMarginsGuide
NSLayoutConstraint.activate([
labelOne.topAnchor.constraint(equalTo: g.topAnchor, constant: 11.0),
labelOne.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
labelOne.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 0.0),
labelTwo.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
labelTwo.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// don't set this here
//labelTwo.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -11.0),
])
// this will satisfy auto-layout
let c = labelTwo.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -11.0)
c.priority = .required - 1
c.isActive = true