I am looking to change the background colour and text colour of the uitableview sections using viewForHeaderInSection like so:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeader = UIView()
let sectionText = UILabel()
sectionHeader.backgroundColor = .blue
sectionText.textColor = .red
sectionText.font = .systemFont(ofSize: 14, weight: .bold)
sectionText.text = painkillersArray[section]["label"] as? String
sectionHeader.addSubview(sectionText)
return sectionHeader
}
The background is working but the text is not appearing. What am I doing wrong?
you need to give frame to both the view and label and also you have to provide heightForHeaderInSection:-
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeader = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tableView.frame.width, height: 50))
let sectionText = UILabel()
sectionText.frame = CGRect.init(x: 5, y: 5, width: sectionHeader.frame.width-10, height: sectionHeader.frame.height-10)
sectionText.text = "Custom Text"
sectionText.font = .systemFont(ofSize: 14, weight: .bold) // my custom font
sectionText.textColor = .red // my custom colour
sectionHeader.addSubview(sectionText)
return sectionHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60 // my custom height
}
===
EDIT: I removed the first code I wrote, which didn't handle autolayout correctly. Morevoer, as #rmaddy pointed out in the comment below, it would be better if you use the provided UITableViewHeaderFooterView as is.
Register
tableView.register(UITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "header")
in viewDidLoad and do
override func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
header?.textLabel?.text = "foo"
return header
}
header?.textLabel?.textColor = .red won't work in the above method, so put customization code in
override func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
header?.textLabel?.text = "foo"
return header
}
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
if let header = view as? UITableViewHeaderFooterView {
header.textLabel?.textColor = .red
header.backgroundView?.backgroundColor = .blue
}
}
===
To use autolayout constraints with more complex headers, provide a custom UITableViewHeaderFooterView subclass:
class CustomTableViewHeaderView: UITableViewHeaderFooterView {
func commonInit() {
let sectionHeader = UIView()
let sectionText = UILabel()
sectionHeader.backgroundColor = .blue
sectionText.textColor = .red
sectionText.font = .systemFont(ofSize: 14, weight: .bold)
sectionText.text = "foo"
sectionHeader.translatesAutoresizingMaskIntoConstraints = false
sectionText.translatesAutoresizingMaskIntoConstraints = false
sectionHeader.addSubview(sectionText)
addSubview(sectionHeader)
NSLayoutConstraint.activate([
sectionHeader.leadingAnchor.constraint(equalTo: leadingAnchor),
sectionHeader.trailingAnchor.constraint(equalTo: trailingAnchor),
sectionHeader.topAnchor.constraint(equalTo: topAnchor),
sectionHeader.bottomAnchor.constraint(equalTo: bottomAnchor),
sectionText.leadingAnchor.constraint(equalTo: sectionHeader.leadingAnchor, constant: 16),
sectionText.trailingAnchor.constraint(equalTo: sectionHeader.trailingAnchor),
sectionText.topAnchor.constraint(equalTo: sectionHeader.topAnchor, constant: 8),
sectionText.bottomAnchor.constraint(equalTo: sectionHeader.bottomAnchor, constant: -8),
])
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
}
and register it in viewDidLoad():
func viewDidLoad() {
super.viewDidLoad()
...
tableView.register(CustomTableViewHeaderView.self, forHeaderFooterViewReuseIdentifier: "header")
...
}
Then simply dequeReusableHeaderFooterView:
func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
return tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
}
Implement heightForHeaderInSection and estimatedHeightForHeaderInSection similarly.
Related
I have a UITableView with custom cells, one cell per section for spacing. I’d like to add an image as the cell background, but I’m not sure how to do so. If I set the cell background image it is obscured by the containerView, and doesn’t have the curved corners. Any suggestions? I’m working with Playgrounds on iPad, so I’d like to do this programmatically. Here’s what I have so far:
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CardCell.self, forCellReuseIdentifier: "CardCell")
tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
tableView.contentInset = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: -30)
tableView.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
}
extension ViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return(5)
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.contentView.layer.masksToBounds = true
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section : Int) -> Int{
tableView.rowHeight = 200
// There is one row in each section
return 1
}
// Setup spacing between sections
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
// Make headers transparent (fill with UIView, set as transparent)
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
return cell
}
}
class CardCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layer.masksToBounds = false
layer.shadowOpacity = 0.23
layer.shadowRadius = 12
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowColor = UIColor.black.cgColor
layer.cornerRadius = 6
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 8
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
PlaygroundPage.current.liveView = ViewController()
You can set background image of any cell like this:
cell.selectedBackgroundView = UIImageView(image: UIImage(named: "cellBgImage.png")?.stretchableImage(withLeftCapWidth: Int(0.0), topCapHeight: Int(5.0)))
I’ve implemented a custom cell for a UITableView, but when I run the Playground it’s just a standard table. It’s probably something stupid simple, but I’m very new to UIKit and somewhat new to Swift.
Also, I’ve tried to implement a “sticky header”, but no matter what I try the header scrolls with the rest of the table.
import UIKit
import PlaygroundSupport
class ViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 19
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CardCell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
cell.messageLabel.text = "yo"
return cell
}
var convoTableView = UITableView()
override func viewDidLoad(){
super.viewDidLoad()
convoTableView = UITableView(frame: self.view.bounds, style:
UITableView.Style.plain)
convoTableView.backgroundColor = UIColor.white
convoTableView.register(CardCell.self, forCellReuseIdentifier: "CardCell")
let header = UIView(frame: CGRect(x: 0, y: 0, width: convoTableView.frame.width, height: 100))
header.backgroundColor = .red
self.convoTableView.delegate = self
self.convoTableView.dataSource = self
let yourLabel = UILabel(frame: CGRect(x: 10, y: 0, width: 100, height: 100))
yourLabel.textColor = UIColor.black
yourLabel.backgroundColor = UIColor.white
yourLabel.text = "mylabel text"
header.addSubview(yourLabel)
convoTableView.tableHeaderView = header
convoTableView.estimatedSectionHeaderHeight = 40.0
self.view.addSubview(convoTableView)
}
}
class CardCell: UITableViewCell {
let messageLabel:UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 14)
label.clipsToBounds = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let dateLabel:UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 8)
label.clipsToBounds = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containerView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
containerView.addSubview(messageLabel)
containerView.addSubview(dateLabel)
self.contentView.addSubview(containerView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
PlaygroundPage.current.liveView = ViewController()
Here's all the code you need here to make a custom cell and sticky header:
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = .purple
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
50
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
19
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
override func viewDidLoad(){
super.viewDidLoad()
tableView.register(CardCell.self, forCellReuseIdentifier: "CardCell")
}
}
class CardCell: UITableViewCell { }
PlaygroundPage.current.liveView = ViewController()
To make the header sticky and non-scrollable with the table view you need to take a different UIView above the table view and give the frames of the table just below the UiView.
customView = MyCustomView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
self.view.addSubview(customView
I have a table with two sections. Property textAligment = .right for header sections. But it's too close to the border of the phone. How to indent from the border closer to the center?
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
header.textLabel?.textColor = .black
header.textLabel?.textAlignment = .right
}
Here's a very simple example...
Add a UITableViewController in Storyboard.
Assign its Custom Class to SectionTableViewController (from the code below).
Use the default cell.
Give the cell an Identifier of "DefCell"
Run the app. This is what you should see:
I've constrained the label to use contentView.layoutMarginsGuide and set the trailing constant to -32
class MySectionHeaderView: UITableViewHeaderFooterView {
let myLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textColor = .black
v.textAlignment = .right
// during development, give it a background color to make it easy to see the frame
//v.backgroundColor = .cyan
return v
}()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
contentView.addSubview(myLabel)
// use layout margins guide
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
myLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
myLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
myLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
// set the constant to provide more trailing space as desired
// Note: must be a negative value
myLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),
])
}
}
class SectionTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MySectionHeaderView.self, forHeaderFooterViewReuseIdentifier: "MyHeader")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "DefCell", for: indexPath)
c.textLabel?.text = "Section: \(indexPath.section) Row: \(indexPath.row)"
return c
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "MyHeader") as? MySectionHeaderView else {
fatalError("Could not dequeue MySectionHeaderView!")
}
v.myLabel.text = "Section \(section)"
return v
}
}
You'll notice these lines in the declaration of myLabel in MySectionHeaderView:
// during development, give it a background color to make it easy to see the frame
//v.backgroundColor = .cyan
This is how it looks when the label has a background color, so you can easily see how the label is laid-out:
I'm trying to create a custom background for my tableview section. The look I'm going for is this:
How do I customize the tableview section background/layer please, rather than just the individual cells?
Edit: Just to clarify - I'm talking about the white background under 'Latest Transactions'. So the top of the section is rounded (as is bottom) but all the rows are rectangular.
Create UITableViewCell subclass and add a UIView with white color. Add left & right padding for the view to the cell. Add UILabel and other UI elements to this newly added view instead of adding in cell or its contentView. Set cell background color as UIColor.groupTableViewBackground
class CustomCell: UITableViewCell {
let bgView = UIView()
let label = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
backgroundColor = .groupTableViewBackground
bgView.backgroundColor = .white
bgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(bgView)
label.translatesAutoresizingMaskIntoConstraints = false
bgView.addSubview(label)
bgView.topAnchor.constraint(equalTo: topAnchor).isActive = true
bgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
bgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
label.heightAnchor.constraint(equalToConstant: 30).isActive = true
label.topAnchor.constraint(equalTo: bgView.topAnchor, constant: 5).isActive = true
label.bottomAnchor.constraint(equalTo: bgView.bottomAnchor, constant: -5).isActive = true
label.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 5).isActive = true
label.trailingAnchor.constraint(equalTo: bgView.trailingAnchor, constant: -5).isActive = true
}
}
Use this cell class in your tableView. And set your view controller background color and tableView background color as UIColor.groupTableViewBackground
In cellForRowAt check if the cell is first or last cell of the section. If it is the first cell of the section, apply corner radius to top left, top right corners. If the cell is the last cell of the section, apply corner radius to bottom left, bottom right corners. If the cell is in the middle remove corner radius.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = .groupTableViewBackground
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
tableView.separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 10)
tableView.tableFooterView = UIView()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section \(section) Title"
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as? CustomCell
?? CustomCell(style: .default, reuseIdentifier: "CustomCell")
if indexPath.row == 0 {//first cell of this section
cell.bgView.layer.cornerRadius = 15.0
cell.bgView.layer.masksToBounds = true
cell.bgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else if indexPath.row == tableView.numberOfRows(inSection: indexPath.section)-1 {//last cell of this section
cell.bgView.layer.cornerRadius = 15.0
cell.bgView.layer.masksToBounds = true
cell.bgView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
} else {
cell.bgView.layer.cornerRadius = 0
}
cell.label.text = "Row \(indexPath.row)"
return cell
}
}
Have a look at func headerView(forSection section: Int) -> UITableViewHeaderFooterView?
headerView(forSection:) - UITableView | Apple Developer
Use UITableViewDelegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
<#code#>
}
This will help you to set the view of each section header programatically.
To create a section header in tableView, use UITableViewDelegate's tableView(_:viewForHeaderInSection:) and tableView(_:heightForHeaderInSection:) methods.
func getHeaderText(for section: Int) -> String? {
switch section {
case 0:
return "Latest Transactions"
default:
return "Other Transactions"
}
//Add other cases for different sections
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: UIScreen.main.bounds.width, height: 70.0)))
headerView.backgroundColor = .clear
let label = UILabel(frame: CGRect(x: 20, y: 0, width: headerView.bounds.width - 40, height: headerView.bounds.height))
label.text = self.getHeaderText(for: section)
label.font = UIFont.systemFont(ofSize: 16.0, weight: .semibold)
headerView.addSubview(label)
return headerView
//Customize the headerView as per your requirement
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}
In the above code, you can configure the header label in getHeaderText(for:) for each section.
Also, I've created the headerView programatically. You can use a .xib for creating a custom headerView as well. That's completely your choice.
You can customize the headerView as per your requirements either programatically or in the interface.
I'm actually using a table view for display some datas and I'm overriding willDisplayHeaderView to add a Custom style for the header. All is working fine except that I can not change the background color of the title header. Here is my code:
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
{
view.backgroundColor = UIColor.clear
let title = UILabel()
title.font = UIFont(name: "System", size: 22)
title.textColor = UIColor(rgba: "#BA0B23")
title.backgroundColor = UIColor.clear
let header = view as! UITableViewHeaderFooterView
header.textLabel?.font=title.font
header.backgroundColor = UIColor.clear
header.textLabel?.textColor=title.textColor
header.textLabel?.textAlignment = NSTextAlignment.center
let sepFrame = CGRect(x: 0, y: header.frame.size.height-1, width: header.frame.size.width, height: 1)
let seperatorView = UIView(frame: sepFrame)
seperatorView.backgroundColor = UIColor(rgba: "#BA0B23")
header.addSubview(seperatorView)
}
I don't know what I'm doing wrong, some help would be appreciated. Thank you
In story board you can take a table view cell, give its custom tableViewCell class and then customise your cell according to design and then in your view controller you can return that cell like this
public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?{
let cell:CustomHeaderTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "header") as! CustomHeaderTableViewCell
let title = uniqueOrders.arrOrders[section] as? String
cell.lblHeaderTitle.text = "Order Id-\(title!)"
return cell
}
func numberOfSections(in tableView: UITableView) -> Int{
return uniqueOrders.arrOrders.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.array[section].count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Order Id- \(uniqueOrders.arrOrders[section])"
}
I think you should focus on using viewForHeaderInSection, rather than willDisplayHeaderView.
Here's what I'd do:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 110;
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
headerView.backgroundColor = UIColor.clearColor()
let title = UILabel()
title.font = UIFont(name: "System", size: 22)
title.textColor = UIColor(rgba: "#BA0B23")
title.backgroundColor = UIColor.clear
headerView.textLabel?.font = title.font
headerView.backgroundColor = UIColor.clear
headerView.textLabel?.textColor = title.textColor
headerView.textLabel?.textAlignment = NSTextAlignment.center
headerView.addsubView(title)
return headerView;
}
hope that helps