Space Between Sections in UITableview - ios

I need to reduce the space between two sections ofUITableView. I looked at this question but the solution doesn't allow for my custom header view because it combines the space of the footer and header.
Here is a picture of the UITableView. The black color is the UITableView background color.

On iOS 15 you may want to reduce the sectionHeaderTopPadding
if #available(iOS 15.0, *) {
tableView.sectionHeaderTopPadding = 0
}

Did you try override this function:
override func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return .leastNormalMagnitude
}

I think you can solve this by adjusting the footer view height to its min: in Storyboard or XIB.
I don't know what you have written in your code for footer height. Sorry if I am wrong.
Possible duplicate of Hide footer view in UITableView

For Swift 4+ you need to implement these two methods
extension MyViewController : UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return UIView()
}
}

For Swift 3
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}

For Swift 5+:
There is some space for the headers and footers by default. That's why I was having the problem of setting an exact separation for the sections.
My solution to having a separation between 2 sections is the following:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 24
} else if section == 1 {
return 32
} else {
return 40
}
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
nil
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
nil
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
CGFloat.leastNormalMagnitude
}
As you see for viewForFooterInSection and viewForHeaderInSection I needed to return nil.
In case you only want to change the footerHeight, just return CGFloat.leastNormalMagnitude for heightForHeaderInSection, and return the heights for each section in heightForFooterInSection.

Along with the answer posted by Icaro I would like to add that you also need to implement the tableView:viewForFooterInSection: method returning nil for the section you want to remove the empty space below
It will then become:
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.001f;
}
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return nil;
}

You need to use the method heightForHeaderInSection for defining the space between header & cell text. You can also change it depending on different sections for eg. at some sections you may need to show more distance & under some, you don't want to show gap. For such case you can use CGFLOAT_MIN which is 0.000001f. Giving you an example, how you can use different section with different header heights:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 0 || section == 2)
{
return 55.0;
}
else
{
return CGFLOAT_MIN;
}
}

This also may help :
override func viewDidLoad() {
super.viewDidLoad()
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
}

Select the tableView in your storyboard/objectCode and ensure that the style is set to Plain, instead of Grouped. You can find this setting in the attributes "Inspector" tab.
let myTableView : UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.register(TableCellClass.self, forCellReuseIdentifier: "cellId")
tableView.backgroundColor = UIColor(red: 123/255, green: 190/255, blue: 120/255, alpha: 1)
tableView.separatorStyle = .none
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()

In Xcode 13.2, you can adjust the height of the header and footer of sections in the storyboard - see screenshot below:

TableView Delegate methods doesn't effect with float value is 0.0f. Try giving a value greater than that.
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.00001f;
}
- (UIView*)tableView:(UITableView*)tableView
viewForFooterInSection:(NSInteger)section {
return [[UIView alloc] initWithFrame:CGRectZero];
}

Rather than implementing the UITableViewDelegate methods and defining the sectionFooterHeight via CGFloat.leastNormalMagnitude, one can alternatively just
tableView.sectionFooterHeight = 0
and the spacing between sections while no footer is present will go away.
The mechanism is that by default this value is set to UITableView.automaticDimension.
As long as
it stays UITableView.automaticDimension
there are no delegate/dataSource methods that implement the configuration of footer i.e. titleForFooterInSection/viewForFooterInSection
table view's style is set to .grouped
then UITableView will deliberately insert a spacing between sections with no view.
You change sectionFooterHeight to 0, the magic goes away.

I just simply had to reduce the top padding for the tableview section header:
tableView.sectionHeaderTopPadding = 0

You can do it by implement the delegate heightForHeaderInSection & heightForFooterInSection.
The return vaule should not be 0, even if the SectionHeader or the height of SectionFooter is 0, it need a very small value, try CGFLOAT_MIN.
for my example:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if (section == [self.dataArray indexOfObject:self.bannerList]) {
return 46;
}
return CGFLOAT_MIN;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return CGFLOAT_MIN;
}

Work for me
tableView.sectionFooterHeight = 10
// ...
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return nil
}

swift 5 iOS 15
self.tableView.estimatedSectionFooterHeight = 16.0 // spacing between Sections

Related

tableView: multiple sections with different style

I am trying to create a single table in Xcode and I want 3 sections in that table each with different headers.
I want to stick only the header of last section when it scrolls on top not the other sections.
Is there a way to do so please suggest me..
1- Yes, you can do that. In numberOfSections method of UITableViewDelegate, specify how many sections you want. And in viewForHeaderInSection method of UITableviewDelegate, provide a custom view for each section.
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// Make custom header view for each section
if section == 0 {
let header1 = UIView()
return header1
} else if section == 1 {
let header2 = UIView()
return header2
} else if section == 2 {
let header3 = UIView()
return header3
}
}
2- No, you can not do that. UITableView does not allow you to specify that which particular header should stick to the top and which one should not.
You can set the UITableView style to Plain. It will stick all the section header while scrolling.
I think that is not possible to stick the particular section header.
You could achieve the same behaviour by having a custom cell that would act as the header in the first two sections. And then remove the actual header for those.
For example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0 || indexPath.section == 1) && indexPath.row == 0 {
return HeaderCell()
}
// return your desired cells (make sure to handle the number of
// rows properly as there is effectively 1 more than normal in
// in the first two sections now)
}
You'll also need to adjust the headers for the first two sections:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 2 { return YourHeaderView() }
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 2 { return yourHeight }
return 0
}
Also, in the numberOfRows delegate function, you should make sure you add 1 (which will be the HeaderCell).

tableview section header is not sticky in multi sectional tableview

I have implemented a tableview (4 sectional) in IOS. Problem is that
I have just added a section header in first section.Other sections don't have a header.First section does not have row (number of rows is 0).other sections have multiple rows.When I scroll , first section's header is not sticky.it is scrolling and out of screen.My table view style is plain.How can I want to make first section's header is always sticky.Code is below.Unfortunatelly tableview is so complicated and I don't want to make it only one section so that I have implemented it multi sectional.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
return tabHeaderView.contentView
}
else {
return UIView()
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return UITableView.automaticDimension
}
else {
return 0 //just first section have a header (tabview)
}
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
func numberOfSections(in tableView: UITableView) -> Int {
if dataReady {
totalSectionCount = getSectionCount()
return totalSectionCount
}
else {
return 1 //permanent because of tabview
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if !dataReady || ApplicationContext.instance.userAuthenticationStatus.value == .semiSecure{
return 1//for shimmer cell and semisecure view
}
else {
return getNumberOfRows(sectionNumber: section)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if ApplicationContext.instance.userAuthenticationStatus.value == .semiSecure {
semisecureViewCell = EarningsSemisecureViewCell()
setSemisecureViewTextInfo(cell: semisecureViewCell)
semisecureViewCell.delegate = self
semisecureViewCell.layoutIfNeeded()
return semisecureViewCell
}
else if !dataReady {
return getShimmerCell()
}
else {
if indexPath.row == 0 && !(selectedTabIndex == .BRANDPOINT && indexPath.section == 1){//marka puan listesinde header cell olmayacak
return getSectionHeaderViewCell(tableView: tableView,sectionNumber: indexPath.section)
}
else {
let cell = getTableViewCell(tableView: tableView, indexPath: indexPath)
cell.layoutIfNeeded()
return cell
}
}
}
Each section header will stick to the top until that section has some rows to display. Once you scroll all the rows for the section up. section header will be replaced by the next section header.
Here you can use one of the two solutions.
Instead of table view section header. Put your view on top of UITableView.
You can use only one section and combine all the rows in it.
From how you are describing your setup, I tend to believe that what you are looking for is the tableHeaderView of the tableView, not the section header.
See either this question, or official documentation for more info.
If that does not meet your requirements, you might wanna consider a custom view on top of the tableView, as is described here.
If the first section must always be sticky when any section is displaying, I will consider to put a custom view on top of tableView. Use autoLayout or UIStackView to let it work as a table header view.

Set margin on top of a tableview in IOS

I'm new to IOS development with swift and I'm having a problem. I need to create a tableview and it looks almost the way I wanted, except for the space at the top of the first section of the table. It has no name but I would like to reduce the space between the top and the first item. What I was able to do is according to the code and image below:
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) {
case 0:
return ""
default:
return self.nameSection2
}
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.white
let headerLabel = UILabel(frame: CGRect(x: 15, y: 8, width:
tableView.bounds.size.width, height: tableView.bounds.size.height))
headerLabel.font = UIFont(name: "Verdana", size: 16)
headerLabel.textColor = UIColor.lightGray
headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
headerLabel.sizeToFit()
headerView.addSubview(headerLabel)
return headerView
}
The 'margin' you see is because the height for both the section headers is the same. The second one looks less-empty as it actually has a title.
You can modify the height for the headers to reduce the space:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 0
case 1:
return 44 //Required height value here
default:
return defaultValue //Any default value
}
}
You need to implement the heightForHeaderInSection so you can collapse that header. See below:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 1.0
} else {
return 32.0
}
}
You can set the appropriate value for the else condition for your needs but this gives you the idea.
Update #1: I found this link in searching that may help as well: http://stackoverflow.com/a/23955420/3965
It recommends using GLFloat's minimum value instead:
if section == 0 {
return CGFloat.leastNormalMagnitude
}
return tableView.sectionHeaderHeight
Implement heightForHeaderInSection and return the height you want for the first section.
Also, you wouldn't normally implement titleForHeaderInSection and viewForHeaderInSection. Just put your switch statement in viewForHeaderInSection to set the text for your label.
And you don't need to put your UILabel into headerView, just return the label. Or instead of UIView, use UITableViewHeaderFooterView.

How to remove UITableView section header separator

I would like to remove (or make them clearColor) UITableView's section header separators. Setting tableView.separatorStyle = .none doesn't work. I've also tried solutions from here, but none of them actually worked for me (maybe because the answers are pretty old). I still get this tiny separator below the section header.
I created the UITableView in Storyboard and add a UITableViewCell there. Then I set it as a header like this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerTableViewCell")
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 1 {
return 49
}
return 0
}
Don't know why you are returning UITableViewCell in viewForHeaderInSection method so may be it is possible that is showing separator of that UITableViewCell. Try returning the contentView of cell instead of cell from viewForHeaderInSection method.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerTableViewCell")
return cell.contentView
}
Try this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
I have solved this by following way:
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.01
}
Turns out overriding layoutSubviews removes all the cell's separators, both section-related and cell-related.
#interface MyCustomCell : UITableViewCell
#end
#implementation MyCustomCell
- (void)layoutSubviews {
// don't call [super layoutSubviews]
// can be left empty
// does not harm other UI elements inside the contentView
}
#end
In multiple-cell sections, you may just want to only remove the Section-related separators (at the top of first cell and at the bottom of last cell), keeping inset, inter-cell, separators shown:
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *view in self.subviews) {
if ([view isEqual:self.contentView]) continue;
view.hidden = view.bounds.size.width == self.bounds.size.width;
}
}

How to hide UITableViewSections in Swift?

I have a static grouped table view that has 5 sections (all the sections have headers, no footers). I created all of this using a Storyboard. Now, how can I hide the first/top UITableViewSection (including the header). I tried making an outlet to the UITableViewSection but it tells me that it is not valid (undeclared type):
#IBOutlet var section: UITableViewSection!
I did it this way because I was planning on doing:
section.hidden = true
Can it not be done this way?
My delegates and data sources are set up 100% correctly.
Swift 5:
You can use the delegate method heightForHeaderInSection
:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (section == 0) {
return 0.0
}
return UITableView.automaticDimension
}
Earlier than Swift 5: Use UITableViewAutomaticDimension instead of UITableView.automaticDimension
If it's not working with height 0.0, use height 0.1
If you want no cells in a particular section, use the delegate method:
func numberOfRowsInSection(section: Int) -> Int {
if (section == 0) {
return 0
}
else {
// return the number of rows you want
}
}
Or to a neater switch-case syntax:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 0.0
default:
return UITableView.automaticDimension
}
}
I tested both and they are working fine.
I also wish you could just make an #IBOutlet to a section and hide it, but sadly it seems not, so...
Based on various suggestions here, I've established the following, which doesn't require any interfering with explicit size values, and preserves whatever you may have set on a storyboard/XIB already. It just makes the header nil and row count 0 for any section you want to hide (which results in a size of 0.0).
Obviously, you can configure sectionShouldBeHidden to work however you need; hiding #1 & #3 are just arbitrary examples.
Swift v5
private func sectionShouldBeHidden(_ section: Int) -> Bool {
switch section {
case 1, 3: return true
default: return false
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if sectionShouldBeHidden(section) {
return nil // Show nothing for the header of hidden sections
} else {
return super.tableView(tableView, titleForHeaderInSection: section) // Use the default header for other sections
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if sectionShouldBeHidden(section) {
return 0 // Don't show any rows for hidden sections
} else {
return super.tableView(tableView, numberOfRowsInSection: section) // Use the default number of rows for other sections
}
}
Update: Unfortunately, the above is only enough if the style of the table view is Plain. When it's Grouped, there's also additional space added between each section, which needs taking care of too.
This extra space is the section's footer, so can be handled like so:
override public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if sectionShouldBeHidden(section) {
return CGFloat.leastNormalMagnitude // Use the smallest possible value for hidden sections
} else {
return super.tableView(tableView, heightForFooterInSection: section) // Use the default footer height for other sections
}
}
I tried all the solutions here with no success. At the end, adding these delegate methods this one worked:
Swift 5:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 4 ? 0 : return super.tableView(tableView, numberOfRowsInSection: section)
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return section == 4 ? 0.1 : super.tableView(tableView, heightForHeaderInSection: section)
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return section == 4 ? 0.1 super.tableView(tableView, heightForFooterInSection: section)
}
Note that you need to return 0.1 in height, returning 0 won't do it.
0.0 did not work for me. I had to do this in order to make it work.
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 0.01
default:
return UITableViewAutomaticDimension
}
}
For group UITableView with static cells only this solution works:
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 0;
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
let count = self.tableView(tableView, numberOfRowsInSection: section)
if count == 0 {
return CGFloat(Double.leastNormalMagnitude)
}
return 44.0
}
For anyone wanting to hide sections because they are using a static grouped tableView with a dynamic number of sections, the solution below may be of help. In my case, each section with data to display needed to have a custom header. Any section that did not have data, needed to be hidden fully.
The answer above was of great help in my scenario. However, for those who don't always know which section(s) will need to be hidden here is a solution for you extending on the above.
In my scenario, I have up to 12 entries in an array that I want to show in up to 12 sections (amongst other sections in a grouped tableView). If there are less than 12 entries to display, I want to hide the unnecessary sections by giving them 0 height and 0 rows. I also wanted to hide the headerView.
To do this, I did the following:
Set up your tableView as per the excellent answer #sasquatch gave
above.
In the numberOfRowsInSection(section: Int) and tableView(_
tableView: UITableView, heightForHeaderInSection section: Int) functions, check whether the rows/height should be 0.
In my case, I was using sections 1 - 12 for my dynamic data so I used code as below:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//For section 0 and 13, just 1 row is ok
if section == 0 || section == 13 {
return 1
}
//For sections 1 - 12, determine if we have data to populate it, or if we should hide it
if section <= dynamicDataToDisplay.count {
return 2
}
//If it's section 1 - 12, but we don't have a corresponding data entry in dynamicDataToDisplay, then just return 0 rows
return 0
}
The code for the heightForHeader function is similar in logic:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 45.0
case 13:
return UITableViewAutomaticDimension
default:
if dynamicDataToDisplay.count >= section {
return 25.0
} else {
return 0.0
}
} //end switch
}
Even after setting up these functions, I found that I was still getting headers appearing for the sections I wanted to hide. I guess I thought that viewForHeaderInSection would not be called if the numberOfRows was 0 and heightOfHeader was also 0, but it was still being called.
Adding the following helped ensure that the header wasn't unnecessarily created:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//set up header for dynamic data sections
if 1 ... 12 ~= section {
if tableView.numberOfRows(inSection: section) == 0 {
return nil
}
//Continue with the header set up for valid sections with rows to display data
......
}
}
This solution might help anyone who is still getting a header being created despite its height and rows being set to 0.

Resources