I'm trying to add a UIview above the tableView but my view is hiding behind the cells. The view should half on footer of section 0 and half on header of section 1, The empty space is tha padding for header.
I have used bringSubviewToFront method but it's not working. Everything that I had tried are commented below.
func setupMyRankView(){
// tableView.addSubview(myRankView)
// tableView.footerView(forSection: 0)?.insertSubview(myRankView, aboveSubview: self.view)
// tableView.footerView(forSection: 0)?.bringSubviewToFront(myRankView)
self.view.bringSubviewToFront(myRankView)
// tableView.cellForRow(at: indexPath)?.insertSubview(myRankView, aboveSubview: self.view)
// tableView.cellForRow(at: indexPath)?.bringSubviewToFront(myRankView)
myRankView.myRankLabel.text = "Hello"
}
As the table view manages its cells and section headers/footers, it is constantly rearranging the z-orders.
What you need to do is bring the "rank" views to the front every time the table updates itself.
One way to do that is by implementing the table view's scrollViewDidScroll
-- this gives you the added advantage of being a good place to calculate the frame(s) for the "rank" view(s).
It will go something like this:
scrollViewDidScroll
and the code will look like this:
// for each of the "rank" views
for (i, v) in rankViews.enumerated() {
// get the rect for the section footer
var r1 = tableView.rectForFooter(inSection: i)
// get the rect for the NEXT section header
let r2 = tableView.rectForHeader(inSection: i + 1)
// set the full rect size to the
// Bottom of the Header minus the Top of the Footer
r1.size.height = r2.maxY - r1.minY
// start with setting the frame of the "rank" view to 50x50
v.frame = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0)
// center it horizontonally and vertically
v.center = CGPoint(x: r1.midX, y: r1.midY)
// bring it to the front
tableView.bringSubviewToFront(v)
}
Here's a complete example you can try out...
Sample "Rank" view
class MyRankView: UIView {
let myRankLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
myRankLabel.textAlignment = .center
myRankLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(myRankLabel)
let g = self
NSLayoutConstraint.activate([
myRankLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
myRankLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
myRankLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
myRankLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
layer.borderColor = UIColor.systemGreen.cgColor
layer.borderWidth = 2
myRankLabel.textColor = .systemGreen
backgroundColor = .white
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = min(bounds.width, bounds.height) * 0.5
}
}
Simple multi-line label cell
class MyTestCell: UITableViewCell {
let theLabel: UILabel = {
let v = UILabel()
v.numberOfLines = 0
return v
}()
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 {
theLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(theLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor),
theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
theLabel.backgroundColor = .yellow
}
}
Reusable section header/footer views
class MyHeaderFooterBaseView: UITableViewHeaderFooterView {
let label = UILabel()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
let g = self.layoutMarginsGuide
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
])
backgroundView = UIView()
}
}
class MyHeaderView: MyHeaderFooterBaseView {
override func commonInit() {
super.commonInit()
label.textAlignment = .center
backgroundView?.backgroundColor = .cyan
}
}
class MyFooterView: MyHeaderFooterBaseView {
override func commonInit() {
super.commonInit()
label.textAlignment = .left
backgroundView?.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
}
}
Sample controller
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView(frame: .zero, style: .grouped)
// we'll use varying
// number of rows per section and
// number of lines per row
let myData: [[Int]] = [
[1, 2, 3, 4],
[3, 2, 1],
[2, 1, 4, 3, 1],
[2, 2, 2, 3],
[1, 1, 5, 1],
[2, 2, 2],
]
var rankViews: [MyRankView] = []
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
tableView.register(MyTestCell.self, forCellReuseIdentifier: "c")
tableView.register(MyHeaderView.self, forHeaderFooterViewReuseIdentifier: "h")
tableView.register(MyFooterView.self, forHeaderFooterViewReuseIdentifier: "f")
tableView.dataSource = self
tableView.delegate = self
tableView.sectionHeaderTopPadding = 20.0
tableView.estimatedSectionHeaderHeight = 60.0
tableView.estimatedSectionFooterHeight = 60.0
for i in 0..<myData.count - 1 {
let v = MyRankView()
v.myRankLabel.text = "\(i)"
tableView.addSubview(v)
rankViews.append(v)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateRankViews()
}
func numberOfSections(in tableView: UITableView) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! MyTestCell
var s: String = "\(indexPath)"
for i in 2..<(myData[indexPath.section][indexPath.row] + 1) {
s += "\nLine \(i)"
}
c.theLabel.text = s
return c
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "h") as! MyHeaderView
v.label.text = "Section Header: \(section)"
return v
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "f") as! MyFooterView
v.label.text = "Section Footer: \(section)"
return v
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
updateRankViews()
}
func updateRankViews() {
// for each of the "rank" views
for (i, v) in rankViews.enumerated() {
// get the rect for the section footer
var r1 = tableView.rectForFooter(inSection: i)
// get the rect for the NEXT section header
let r2 = tableView.rectForHeader(inSection: i + 1)
// set the full rect size to the
// Bottom of the Header minus the Top of the Footer
r1.size.height = r2.maxY - r1.minY
// start with setting the frame of the "rank" view to 50x50
v.frame = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0)
// center it horizontonally and vertically
v.center = CGPoint(x: r1.midX, y: r1.midY)
// bring it to the front
tableView.bringSubviewToFront(v)
}
}
}
and it looks like this when running: