How to adjust or remove grouped tableView Header size - swift

I have a grouped tableView and it has four sections , I would like to reduce the size of the header section in some section headers and I would like to remove the section header in other sections. I have tried solutions in most questions similar to mine but nothing works.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
tableView.separatorStyle = .none
tableView.clipsToBounds = true
self.tableView.sectionHeaderHeight = UITableView.automaticDimension
self.tableView.estimatedSectionHeaderHeight = 200
self.registerTableViewCells()
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
(view as? UITableViewHeaderFooterView)?.textLabel?.textColor = UIColor.gray
if section == 0 {
(view as? UITableViewHeaderFooterView)?.contentView.backgroundColor = UIColor.white
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
switch section {
case 0:
let headerView = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "\(TestHeaderView.self)" ) as! TestHeaderView
return headerView
case 1, 2, 3 :
return nil
default:
return nil
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0, 1, 2:
return nil
case 3:
return " some title"
default:
return nil
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return UITableView.automaticDimension
case 1:
return CGFloat(10.0)
case 2:
return CGFloat(10.0)
case 3:
return CGFloat(43.0)
default:
return 0
}
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNonzeroMagnitude
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 250
}
I would like to have the tableView section headers to have specific heights as highlighted in heightForHeaderInSection.

You don't seem to be setting the dataSource or tableView delegates (unless it is happening in some part of the code not shown). These will be needed for this (and most dynamic tableview functions).
For headers/footers that won't change size it is reasonably straightforward to create the them - return the view you want to use from tableView(_:viewForHeaderInSection:) and the height you want in tableView(_:heightForHeaderInSection:). Set these to nil and 0 respectively to hide a header.
A simple example that provides different size headers for section 0 & 2, no header for section 1, and hides all footers. I've set the headers background colours to allow them to stand out, and not provided the other methods such as cellForRowAt. Note the height is controlled by the height method, not the frame height.
class MyVC1: UIViewController {
var mapa: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
title = "Dummy TableView"
view.backgroundColor = .lightGray
let tableView = UITableView(frame: CGRect(x:50, y:100, width: 300, height: 680), style: .grouped)
view.addSubview(tableView)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
}
}
//add dataSource and delegate support
extension MyVC1: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
5
}
func numberOfSections(in tableView: UITableView) -> Int {
3
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
switch section {
case 0:
let v = UIView(frame: CGRect(x: 10, y: 10, width: tableView.frame.width - 20, height: 90))
v.backgroundColor = .purple
return v
case 1: return nil
case 2:
let v = UIView(frame: CGRect(x: 10, y: 10, width: tableView.frame.width - 20, height: 20))
v.backgroundColor = .magenta
return v
default: fatalError()
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0: return CGFloat(60)
case 1: return CGFloat(0)
case 2: return CGFloat(100)
default: fatalError()
}
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
nil
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
0
}
}

have you added UITableViewdelegate and UITableViewDataSource ?
or try this self.tableView.estimatedSectionHeaderHeight = 80

Related

How to add corner radius and a background to a whole section including its header and cells in UI Table View

How do I add corner radius and background to both the section header and its cell together like in the iOS weather app?
My table view data source and delegate-
extension HomeViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
10
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: HourlyForecastViewTableViewCell.identifier) else { return UITableViewCell() }
cell.layer.backgroundColor = UIColor.blue.cgColor
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
200
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return HourlyForecastHeaderView()
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
You can tryout this below code, here I just explained how to use UIRectCorner to apply corner radius to the specific edge.
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let kCellId = "kCellId"
var lCell = tableView.dequeueReusableCell(withIdentifier: kCellId)
if lCell == nil {
lCell = UITableViewCell(style: .default, reuseIdentifier: kCellId)
lCell?.textLabel?.text = "Row - \(indexPath.row)"
}
lCell?.backgroundColor = .orange
return lCell!
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let kHeaderId = "kHeaderId"
var header = tableView.dequeueReusableHeaderFooterView(withIdentifier: kHeaderId)
if header == nil {
header = HeaderView(reuseIdentifier: kHeaderId)
header?.frame = CGRect(origin: .zero, size: CGSize(width: tableView.bounds.size.width, height: 35))
header?.tintColor = .orange
let titleLabel = UILabel()
titleLabel.text = "Section - \(section)"
titleLabel.sizeToFit()
titleLabel.frame = CGRect(origin: CGPoint(x: 20, y: 0), size: CGSize(width: tableView.bounds.size.width-20, height: 35))
header?.addSubview(titleLabel)
}
return header
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
view.applyCorners(to: [.topLeft, .topRight], with: view.bounds)
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let isLastCell = tableView.numberOfRows(inSection: indexPath.section)-1 == indexPath.row
if isLastCell {
cell.applyCorners(to: [.bottomLeft, .bottomRight], with: cell.bounds)
}else{
cell.noCorners()
}
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
50
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
35
}
}
class HeaderView: UITableViewHeaderFooterView {
}
extension UIView {
func noCorners() {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
let shape = CAShapeLayer()
shape.path = path.cgPath
layer.mask = shape
}
func applyCorners(to: UIRectCorner, with rect: CGRect) {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: to, cornerRadii: CGSize(width: 10, height: 10))
let shape = CAShapeLayer()
shape.path = path.cgPath
layer.mask = shape
}
}

Why does UITableView show the same view for each cell?

I have been trying to implement a UISegmentedControl in the first cell of a UITableView and it has worked however it shows for each cell following the first one. How do I fix this
import UIKit
class InfoTableViewController: UITableViewController {
convenience init() {
self.init(style: UITableView.Style.grouped)
}
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 5
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
if indexPath.row == 0 {
let genderItems = ["Male", "Female"]
let genderControl = UISegmentedControl(items: genderItems)
genderControl.addTarget(self, action: #selector(genderChanged), for: .valueChanged)
cell.contentView.addSubview(genderControl)
genderControl.translatesAutoresizingMaskIntoConstraints = false
genderControl.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true
genderControl.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
}
return cell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0{
return view.frame.height/3
}
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let imageView: UIImageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = self.image
return imageView
}
return nil
}
#objc func genderChanged(){
}
}
Solution #1
Remove this
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 5
}
and return 5 here
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 5
}
Solution #2
Replace
if indexPath.row == 0 {
to
if indexPath.section == 0 {
in both options you should implement else for that if to return the other needed cell
also move this line to viewDidLoad
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")

How to make Automatic Height Header in UITableView?

Hello i try to make expand/collapse using UITableView but i have problem with Header. I have try all tutorial but not working. This is my sample code:
import UIKit
import SafariServices
class AboutViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var data = [
DataModal(headerName: "Apa itu Brevir?", subType: ["Brevir adalah bla..bla"], isExpandable: false),
DataModal(headerName: "Apa isi Brevir?", subType: ["Garis besarnya adalah bla..bla..blaa...bla..bla..blaa...bla..bla..blaa...bla..bla..blaa"], isExpandable: false),
DataModal(headerName: "Mengapa 7x Sehari?", subType: ["Tujuh Kali dalam bla..bla"], isExpandable: false),
DataModal(headerName: "Ibadat apa saja yang termaksud dalam 7x sehari tersebut?", subType: ["a. Ibadat Pembukaan", "b. Ibadat Pembukaan", "c. Ibadat Pembukaan", "d. Ibadat Pembukaan", "e. Ibadat Pembukaan", "f. Ibadat Pembukaan", "g. Ibadat Pembukaan"], isExpandable: false)]
override func viewDidLoad() {
super.viewDidLoad()
tblView.tableFooterView = UIView()
}
}
extension AboutViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = HeaderView(frame: CGRect(x: 10, y: 10, width: tblView.frame.size.width - 20, height: 40))
headerView.delegate = self
headerView.secIndex = section
headerView.btn.setTitle(data[section].headerName, for: .normal)
return headerView
}
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if data[section].isExpandable {
return data[section].subType.count
} else {
return Int(UITableView.automaticDimension)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = data[indexPath.section].subType[indexPath.row]
return cell
}
override func viewWillAppear(_ animated: Bool) {
tblView.estimatedSectionHeaderHeight = 40
tblView.sectionHeaderHeight = UITableView.automaticDimension
}
}
extension AboutViewController: HeaderDelegate {
func callHeader(idx: Int) {
data[idx].isExpandable = !data[idx].isExpandable
tblView.reloadSections([idx], with: .automatic)
}
}
Since you are using button, it won't expand automatically.
Use a label instead and set its numberOfLines = 0.
Later add Button over it.
Also implement table view sections delegate/dataSource methods:
heightForHeaderInSection return 50 //minimum height
estimateHeightForHeaderSection return UITableView.automaticDimension // automatic height
Try this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let board = UIView()
let headerView = HeaderView(frame: CGRect(x: 10, y: 10, width: tablView.frame.size.width - 20, height: 40)
headerView.delegate = self
headerView.secIndex = section
headerView.btn.setTitle(data[section].headerName, for: .normal)
board.addSubview(headerView)
return board
}
First you have to calculate height and use "UITableViewDelegate" methods as follows
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int)
-> CGFloat {
return ( your calculated height for header)
}

Grouped UITableView with Shadow and Corner Radius?

I was wondering how one might go about creating a grouped UITableView with sections that have a drop shadow and rounded corners. Also, the section header is selectable, so when the user clicks the header the cells in that section should collapse; therefore, the shadow and rounded corners will need to change to the new size of the section, just the header. Does anybody know how to do what I am talking about? I have attached a UI mockup for reference as well as some code.
Here is a UI Mockup of what I wish to design:
This is the code I have to set up the tableView:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
return nil
} else if settings[section - 1].isEmpty == false {
let headerView = Bundle.main.loadNibNamed("HeaderTableViewCell", owner: self, options: nil)?.first as! HeaderTableViewCell
let settingCategories = ["Account", "Notifications", "Personalized Feed", "Color Theme"]
headerView.settingCategoryTitle.text = settingCategories[section - 1]
let totalCellHeight = CGFloat(settings[section - 1].count * 63)
let height = headerView.frame.height + totalCellHeight
print("Needed Height: \(height)")
headerView.clipsToBounds = false
headerView.headerBackground.frame.size.height = height
print("HeaderView Height: \(headerView.headerBackground.frame.size.height)")
headerView.headerBackground.layer.shadowRadius = 10
headerView.headerBackground.layer.shadowOffset = CGSize(width: 0, height: 1)
headerView.headerBackground.layer.shadowColor = UIColor.black.cgColor
headerView.headerBackground.layer.shadowOpacity = 0.1
headerView.headerBackground.backgroundColor = UIColor(named: "Background")
headerView.headerBackground.layer.cornerRadius = 10
headerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDropDown)))
return headerView
} else {
return nil
}
}
#objc func handleDropDown() {
print("Handle Drop Down")
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 0.0001
} else {
return 54
}
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if section == 0 {
return 10
} else {
return 20
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return settings.count + 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return settings[section - 1].count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "SettingTitleCell", for: indexPath) as! SettingsTitleTableViewCell
cell.titleLabel.text = "Settings"
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "SettingCell", for: indexPath) as! SettingTableViewCell
cell.setSetting(setting: settings[indexPath.section - 1][indexPath.row])
print("Index Path: \(indexPath.row) SettingCount: \(settings[indexPath.section - 1].count)")
return cell
}
}
That code produces this result:
I would love some help on how to make this work the way I have shown in the design. Thanks ahead of time for any help.
For collapse your header you can use this: https://github.com/jeantimex/ios-swift-collapsible-table-section
i used nested tableview to resolve it uibezierpath may be render incorrect layouts.

Resize table header when using dynamic types

I have a TableViewController with a header.
This header is a container, which links to another storyboard named Header.storyboard
Header.storyboard contains a stack view with some labels, that are dynamically typed.
Labels text comes from DB.
No regards to the text dimension or size I want to visualise it correctly.
I've used some answers from SO to get the header correctly resize, but without luck:
import UIKit
class TableViewController: UITableViewController {
#IBOutlet weak var tableHeaderView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the header view
if let headerView = tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableHeaderView = headerView
}
}
}
}
Table view header can be automatically resized
but with containers it likely will not work
use custom view instead for viewForHeaderInSection
class ViewController: UITableViewController {
override func viewDidLoad() {
tableView.estimatedSectionHeaderHeight = 10
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let lab = UILabel()
lab.text = "text \(section)"
lab.font = UIFont.systemFont(ofSize: 10 * CGFloat(section) + 1)
return lab
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
//this method overriding is not necessary
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
}
First, instead of giving the height to the view, you should give the label a height
class ViewController : UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.backgroundColor = UIColor.yellow
cell.textLabel?.text = "Baran"
return cell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
return heightForView()
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView : UIView = UIView(frame: CGRect(x: 0, y: 20, width: self.view.frame.width, height: self.heightForView()))
headerView.backgroundColor = UIColor.black
let label : UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.heightForView()))
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.center
label.text = "Size To Fit Tutorial"
label.font = UIFont(name: "Helvetica", size: 50)
label.textColor = UIColor.white
headerView.addSubview(label)
return headerView
}
//Self Sizing height ....
func heightForLabel(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byCharWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
func heightForView() -> CGFloat{
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let text = "Size To Fit Tutorial"
let font : UIFont!
switch UIDevice.current.userInterfaceIdiom {
case .pad:
font = UIFont(name: "Helvetica", size: 35)
case .phone:
font = UIFont(name: "Helvetica", size: 50)
default:
font = UIFont(name: "Helvetica", size: 24)
}
let height = heightForLabel(text: text, font: font, width: screenWidth)
return height
}
}
Hope this helps
try to implement
tableView(_ tableView: UITableView, heightForHeaderInSection section: Int)
and
tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)
your code should look like this :
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return tableHeaderView.bounds.size.height
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return tableHeaderView
}
For Xcode 11, iOS 13 - I just had to tick these two boxes in Interface Builder: