'How can I make section header like Android sticky view?
I am using the section header of the tableview. https://www.youtube.com/watch?v=U3gqR4dRiT8
If you look at it, almost at the end
When the section header goes up, the existing top section header goes up and collides and disappears.
And the text in the top section header is changed and floats But when I run the sample, the text changes. It doesn't seem to have the effect of pushing up the section header. What should I do?
import UIKit
class MyTableviewControllerTableViewController: UITableViewController {
private let numberOfSections = 3
var meat = ["Beef","Turkey","Fish", "Lamb", "Chicken", "Pork", "Beef","Turkey","Fish", "Lamb", "Chicken", "Pork"]
var fruit = ["Apple","Banana","Cherry","Apple","Banana","Cherry","Apple","Banana","Cherry"]
var vegetable = ["Lettuce","Broccoli","Cauliflower","Lettuce","Broccoli","Cauliflower","Lettuce","Broccoli","Cauliflower"]
override func viewDidLoad() {
super.viewDidLoad()
// make the header sticky
self.automaticallyAdjustsScrollViewInsets = false
}
// MARK: - Table view data source
// return the number of sections
override func numberOfSections(in tableView: UITableView) -> Int {
return numberOfSections
}
// return the number of rows in the specified section
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rowCount = 0
switch (section) {
case 0:
rowCount = meat.count
case 1:
rowCount = fruit.count
case 2:
rowCount = vegetable.count
default:
rowCount = 0
}
return rowCount
}
// Content Cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
switch (indexPath.section) {
case 0:
cell.textLabel?.text = meat[indexPath.row]
case 1:
cell.textLabel?.text = fruit[indexPath.row]
case 2:
cell.textLabel?.text = vegetable[indexPath.row]
default:
cell.textLabel?.text = "Other"
}
return cell
}
// Header Cell
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as! CustomTableViewHeaderCell
headerCell.backgroundColor = UIColor.gray
switch (section) {
case 0:
headerCell.headerLabel.text = "Meat";
case 1:
headerCell.headerLabel.text = "Fruit";
case 2:
headerCell.headerLabel.text = "Vegetable";
default:
headerCell.headerLabel.text = "Other";
}
return headerCell
}
// Footer Cell
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: tableView.frame.size.width, height: 20))
let footerView = UIView(frame: rect)
footerView.backgroundColor = UIColor.darkGray
return footerView
}
// footer height
override func tableView(_ : UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20.0
}
Solution 1:[1]
You can do it programmatically, and the result is perfect... Declare your table view and carry under your UIViewController (Attention, it's No UITableViewController) class:
class YourControllerClass: UIViewController {
let tableView = UITableView()
let cellId = "cellId"
let headerId = "headerId"
var meat = ["Beef","Turkey","Fish", "Lamb", "Chicken", "Pork", "Beef","Turkey","Fish", "Lamb", "Chicken", "Pork"]
var fruit = ["Apple","Banana","Cherry","Apple","Banana","Cherry","Apple","Banana","Cherry"]
var vegetable = ["Lettuce","Broccoli","Cauliflower","Lettuce","Broccoli","Cauliflower","Lettuce","Broccoli","Cauliflower"]
now in viewDidLoad configure nav bar and set tableview attributes and constraints:
override func viewDidLoad() {
super.viewDidLoad()
configureNavigationBar(largeTitleColor: .yourPreferColor, backgoundColor: .ultraDark, tintColor: .fuxiaRed, title: "My Title", preferredLargeTitle: true) // I use my function to configure navBar
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.register(HeaderCell.self, forCellReuseIdentifier: headerId)
tableView.backgroundColor = .yourPreferColor
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor(white: 1, alpha: 0.2)
tableView.tableFooterView = UIView()
tableView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
if #available(iOS 15.0, *) {
tableView.sectionHeaderTopPadding = 0
tableView.sectionFooterHeight = 0
} else {
UITableView.appearance().sectionHeaderTopPadding = CGFloat(0)
UITableView.appearance().sectionFooterHeight = CGFloat(0)
}
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
Now create an extension for tableView delegate and datasource:
extension NavBarAppearenceController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50 // header height
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
let headerCell = tableView.dequeueReusableCell(withIdentifier: headerId) as! HeaderCell // header cell
headerCell.translatesAutoresizingMaskIntoConstraints = false
headerView.addSubview(headerCell)
headerCell.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
headerCell.leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
headerCell.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
headerCell.bottomAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
switch section {
case 0:
headerCell.label.text = "Meat"
case 1:
headerCell.label.text = "Fruit"
default:
headerCell.label.text = "Vegetables"
}
return headerView
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return meat.count
case 1:
return fruit.count
case 2:
return vegetable.count
default:
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let mySection = indexPath.section
switch mySection {
case 0:
cell.textLabel?.text = "\(meat[indexPath.row])"
case 1:
cell.textLabel?.text = "\(fruit[indexPath.row])"
default:
cell.textLabel?.text = "\(vegetable[indexPath.row])"
}
cell.contentView.backgroundColor = .yourPreferColor
cell.textLabel?.textColor = .white
cell.selectionStyle = .none
return cell
}
}
Create your custom header cell:
class HeaderCell: UITableViewCell {
let myView: UIView = {
let v = UIView()
v.backgroundColor = .yourPreferColor
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let label = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .yourPreferColor
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .bold)
label.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(myView)
myView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
myView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
myView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
myView.addSubview(label)
label.topAnchor.constraint(equalTo: myView.topAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: myView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: myView.trailingAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: myView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This is the result:
BONUS
This is my navBar configuration func:
func configureNavigationBar(largeTitleColor: UIColor, backgoundColor: UIColor, tintColor: UIColor, title: String, preferredLargeTitle: Bool) {
if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.titleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.backgroundColor = backgoundColor
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.compactAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
navigationController?.navigationBar.prefersLargeTitles = preferredLargeTitle
navigationItem.largeTitleDisplayMode = .always
navigationController?.navigationBar.tintColor = tintColor
navigationItem.title = title
} else {
// Fallback on earlier versions
navigationController?.navigationBar.barTintColor = backgoundColor
navigationController?.navigationBar.tintColor = tintColor
navigationController?.navigationBar.isTranslucent = false
navigationItem.title = title
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

