214

I'm using a UITableView to layout content 'pages'. I'm using the headers of the table view to layout certain images etc. and I'd prefer it if they didn't float but stayed static as they do when the style is set to UITableViewStyleGrouped.

Other then using UITableViewStyleGrouped, is there a way to do this? I'd like to avoid using grouped as it adds a margin down all my cells and requires disabling of the background view for each of the cells. I'd like full control of my layout. Ideally they'd be a "UITableViewStyleBareBones", but I didn't see that option in the docs...

Many thanks,

TheNeil
  • 3,321
  • 2
  • 27
  • 52
Tricky
  • 7,025
  • 5
  • 33
  • 43
  • 1
    http://stackoverflow.com/questions/664781/change-default-scrolling-behavior-of-uitableview-section-header/3984585#3984585 – Fattie Nov 14 '13 at 12:33
  • 65
    Use table style **UITableViewStyleGrouped** - this is the answer for all who are looking for disabling floating headers and don't read the whole question (it happened to me...). – average Joe Jan 24 '15 at 14:12
  • @Kasztan This is a zero line solution that works great! Thanks. – Lee Fastenau Mar 13 '15 at 23:42
  • Using empty sections is also a great choice https://stackoverflow.com/a/8350326/456536 – Míng May 28 '21 at 12:06

31 Answers31

368

A probably easier way to achieve this:

Objective-C:

CGFloat dummyViewHeight = 40;
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, dummyViewHeight)];
self.tableView.tableHeaderView = dummyView;
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0);

Swift:

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)

Section headers will now scroll just like any regular cell.

John Codeos
  • 1,060
  • 1
  • 13
  • 17
samvermette
  • 40,269
  • 27
  • 112
  • 144
  • 7
    This friggin works like a charm!! The presence of a table header view header stops the section headers from floating. And an invisible header with appropriate content insets for the table does the trick .. Thank you so much! wonder why this answer's not getting much attention – Shyam Bhat Jun 11 '12 at 11:45
  • 4
    Still don't believe it! No private APIs, no digging into the classes, nothing! I have been facing this problem for ages and have been adding tableviews on top of scroll views (with the tableview scroll disabled), which would make the table headers scroll normally. but just started to search if apple's provided any new methods lately to fix this.. so glad i stumbled upon your answer. thanks again – Shyam Bhat Jun 11 '12 at 11:53
  • Yeah, I just returned a UILabel in viewForHeaderInSection with the same height as the dragged UIView and set the contentInset to the same negative height, and blammo...samvermette, you're a saver mate! – whyoz Sep 20 '12 at 23:38
  • 1
    Stumbled upon this solution as well... But really thinking about it, isn't it just taking advantage of a bug? Ideally the contentInset should also inset the point at which the headers scroll. Apple still hasn't patched it 2 years later! – Piotr Tomasik Oct 30 '12 at 11:55
  • 13
    the only drawback I saw is that top header will not be shown anymore (you can see it only through scrolling up). At least for me it is not shown – Ruzard Dec 15 '12 at 16:49
  • 1
    Unfortunately, I'm seeing the same behavior as @Ruzard. – Joel Martinez Jan 09 '13 at 15:19
  • This solutions worked well for me. Apparently, here's another one completely different: http://stackoverflow.com/a/3984585/707984 – onekiloparsec Mar 01 '13 at 16:21
  • 1
    you can make the original top table view header appear by raising its height by 100. This cancels out content inset setting influence on header, but still allows disabling floating headers. – Vilém Kurz Mar 11 '13 at 09:41
  • Another drawback: This completely confuses any index bar you might have considered using. – Stavash Jun 10 '13 at 13:34
  • I got this working in code with: `self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 24)];` and setting the content-inset-top to -24 in IB. How do you "Drag a UIView onto your TableView to make it its header view." in IB? There is no outlet for the `tableHeaderView` property. – jhabbott Jul 27 '13 at 12:46
  • 5
    You can still show the tableHeaderView of the table by increasing it's height on the top, by a number equal to the height of section header and reduce the content inset by the same number. – Shyam Bhat Aug 01 '13 at 13:57
  • It's old post but could not stop my self to write this. Awesome! Thanks. – deepax11 Dec 01 '15 at 22:26
  • For me I had to use positive dummy height for my inset but This helped a ton! – NSGangster May 02 '16 at 14:09
  • 2
    I wish I could upvote this again, I'm fairly certain this is at least the third time I've ended up on this question and this has solved my problem. – Ell Neal Nov 16 '16 at 12:11
  • These are the kind of workarounds that I just laugh at, it feels so bad doing this but I love it. Thank you man – lennartk Aug 05 '18 at 21:26
  • 2
    I've found this solution super hacky. It doesn't work well if your headers can vary in size (like when you support dynamic type). – Clément Cardonnel Jan 14 '19 at 15:11
  • 1
    This does not work with a refresh control. The control will be off screen. – Daniel Jan 23 '20 at 15:41
311

(For who ever got here due to wrong table style) Change Table style from plain to grouped, via the attributes inspector, or via code:

let tableView = UITableView(frame: .zero, style: .grouped)
david72
  • 7,151
  • 3
  • 37
  • 59
77

WARNING: this solution implements a reserved API method. This could prevent the app from being approved by Apple for distribution on the AppStore.

I've described the private methods that turns of section headers floating in my blog

Basically, you just need to subclass UITableView and return NO in two of its methods:

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;
shim
  • 9,289
  • 12
  • 69
  • 108
  • 50
    An answer doesn't deserve a downvote just because it references private APIs (not everybody is writing code to be submitted to the AppStore). This is doubly true when the answer happens to be correct. Subclassing (or, more easily, making a UITableView category) that returns NO for these methods stops headers from floating. If you'd like to see this added as a public API, please file a bug report: http://bugreport.apple.com – jemmons Jul 19 '10 at 00:18
  • 5
    If we implement that function in an App, how would Apple realize you're using a private API? From all they know, I just implemented a method in a subclass of UITableView, and they can't even see that without de-assembling the code, and it wouldn't be so easy. It's not CALLING a method in a private API, it's just implementing one.... – Javier Soto Jan 25 '12 at 19:46
  • 1
    Has anyone used this method and got approved in the App Store? – nonamelive Jun 21 '13 at 03:57
  • Private Methods! be wary, the app will get rejected. Check my answer below for the correct way to achieve the effect – anemo Jul 29 '13 at 22:13
  • 3
    @jemmons if you don't want me to downvote your answer mark it as "private API is used here". Sort of disclaimer :) – kas-kad Oct 29 '13 at 11:53
  • 2
    Your "my blog" link is broken – MartinMoizard May 22 '14 at 14:43
  • Do you mean return NO?! – hasan Mar 18 '15 at 16:27
  • 14
    @jemmons "not everybody is writing code to be submitted to the AppStore" Appstore is not really the problem. The problem is apple can change the private APIs without communicating or deprecating which will break your app. That's a better reason on why you shouldn't use private APIs than getting rejected by Apple – aryaxt Aug 17 '15 at 18:04
  • Guys, be aware of using this method with Swift 4. Default settings of Swift 4 project in Xcode 9 has `@objc` inference property disabled. So, you should to mark these methods like `@objc` manually otherwise it will lead to undefined behavior. Hope, this tip can help you. – ostap_holub Sep 27 '17 at 10:50
38

In your Interface Builder click on your problem Table View

problem table view

Then navigate to Attributes Inspector and change Style Plain to Grouped ;) Easy

easy

HannahCarney
  • 3,441
  • 2
  • 26
  • 32
28

Ok, i know it is late but i had to do it. I have spent 10 hours by now searching for a working solution but did not find a complete answer. Did found some hints but difficult for starters to understand. So i had to put in my 2 cents and complete the answer.

As it has been suggested in the few of the answers the only working solution that i was able to implement is by inserting normal cells in the table view and handle them as Section Headers, but the better way to achieve it is by inserting these cells at row 0 of every section. This way we can handle these custom non-floating headers very easily.

So, the steps are.

  1. Implement UITableView with style UITableViewStylePlain.

    -(void) loadView
    {
        [super loadView];
    
        UITableView *tblView =[[UITableView alloc] initWithFrame:CGRectMake(0, frame.origin.y, frame.size.width, frame.size.height-44-61-frame.origin.y) style:UITableViewStylePlain];
        tblView.delegate=self;
        tblView.dataSource=self;
        tblView.tag=2;
        tblView.backgroundColor=[UIColor clearColor];
        tblView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }
    
  2. Implement titleForHeaderInSection as usual ( you can get this value by using your own logic, but I prefer to use standard delegates ).

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSString *headerTitle = [sectionArray objectAtIndex:section];
        return headerTitle;
    }
    
  3. Immplement numberOfSectionsInTableView as usual

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        int sectionCount = [sectionArray count];
        return sectionCount;
    }
    
  4. Implement numberOfRowsInSection as usual.

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
        int rowCount = [[cellArray objectAtIndex:section] count];
        return rowCount +1; //+1 for the extra row which we will fake for the Section Header
    }
    
  5. Return 0.0f in heightForHeaderInSection.

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
        return 0.0f;
    }
    
  6. DO NOT implement viewForHeaderInSection. Remove the method completely instead of returning nil.

  7. In heightForRowAtIndexPath. Check if(indexpath.row == 0) and return the desired cell height for the section header, else return the height of the cell.

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if(indexPath.row == 0)
        {
            return 80; //Height for the section header
        }
        else
        {
            return 70; //Height for the normal cell
        }
    }
    
  8. Now in cellForRowAtIndexPath, check if(indexpath.row == 0) and implement the cell as you want the section header to be and set the selection style to none. ELSE implement the cell as you want the normal cell to be.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"];
            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SectionCell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleNone; //So that the section header does not appear selected
    
                cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SectionHeaderBackground"]];
            }
    
            cell.textLabel.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:indexPath.section];
    
            return cell;
        }
        else
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    
            if (cell == nil) 
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleGray; //So that the normal cell looks selected
    
                cell.backgroundView =[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellBackground"]]autorelease];
                cell.selectedBackgroundView=[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SelectedCellBackground"]] autorelease];
            }
    
            cell.textLabel.text = [[cellArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row -1]; //row -1 to compensate for the extra header row
    
            return cell;
        }
    }
    
  9. Now implement willSelectRowAtIndexPath and return nil if indexpath.row == 0. This will care that didSelectRowAtIndexPath never gets fired for the Section header row.

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            return nil;
        }
    
        return indexPath;
    }
    
  10. And finally in didSelectRowAtIndexPath, check if(indexpath.row != 0) and proceed.

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row != 0)
        {
            int row = indexPath.row -1; //Now use 'row' in place of indexPath.row
    
            //Do what ever you want the selection to perform
        }
    }
    

With this you are done. You now have a perfectly scrolling, non-floating section header.

anemo
  • 1,327
  • 1
  • 14
  • 28
  • 1
    Implemented 'willSelectRowAtIndexPath' so that the control never reaches 'didSelectRowAtIndexPath' on selecting the Section Header row. – anemo Jan 24 '13 at 19:56
  • Actually, I fold + unfold the rows when the header is selected. But good to know you can do that – Yariv Nissim Jan 24 '13 at 20:36
  • Great answer, but why the 'if (indexPath.row != 0)' check in 'didSelectRowAtIndexPath'? Is it just to be extra secure? The row can never be 0 with your 'willSelectRowAtIndexPath' implementation, right? – Glenn85 Aug 21 '13 at 12:28
  • 1
    @Glenn85, Sorry for the late reply. Yes it is just for an extra security. – anemo Nov 28 '13 at 03:15
26

You should be able to fake this by using a custom cell to do your header rows. These will then scroll like any other cell in the table view.

You just need to add some logic in your cellForRowAtIndexPath to return the right cell type when it is a header row.

You'll probably have to manage your sections yourself though, i.e. have everything in one section and fake the headers. (You could also try returning a hidden view for the header view, but I don't know if that will work)

shim
  • 9,289
  • 12
  • 69
  • 108
frankodwyer
  • 13,948
  • 9
  • 50
  • 70
  • 12
    Yeah... it seems a bit of a hack though. This seems like an oversight an Apple's part, as clearly the code is there to do it, we just need to be able to set a flag. – Tricky Jul 02 '09 at 13:54
  • 2
    Check my answer below for the correct way to achieve the effect without any special hack, just manage the first cell of every section as its section header! – anemo Jul 29 '13 at 22:17
19

Change your TableView Style:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

As per apple documentation for UITableView:

UITableViewStylePlain- A plain table view. Any section headers or footers are displayed as inline separators and float when the table view is scrolled.

UITableViewStyleGrouped- A table view whose sections present distinct groups of rows. The section headers and footers do not float.

Zoe
  • 27,060
  • 21
  • 118
  • 148
Ash
  • 5,525
  • 1
  • 40
  • 34
  • 2
    ☝️ This should be marked as correct answer. Also, for storyboard lovers, you have this property in storyboard Attribute inspector when you select your UITableView – Nikola Markovic Nov 29 '20 at 20:14
17

The interesting thing about UITableViewStyleGrouped is that the tableView adds the style to the cells and not to the TableView.

The style is added as backgroundView to the cells as a class called UIGroupTableViewCellBackground which handles drawing different background according to the position of the cell in the section.

So a very simple solution will be to use UITableViewStyleGrouped, set the backgroundColor of the table to clearColor, and simply replace the backgroundView of the cell in cellForRow:

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];
adamsiton
  • 3,642
  • 32
  • 34
6

There is another tricky way. The main idea is to double the section number, and first one only shows the headerView while the second one shows the real cells.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return sectionCount * 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }
    return _rowCount;
}

What need to do then is to implement the headerInSection delegates:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerview;
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerheight;
    }
    return 0;
}

This approach also has little impact on your datasources:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    int real_section = (int)indexPath.section / 2;
    //your code
}

Comparing with other approaches, this way is safe while not changing the frame or contentInsets of the tableview. Hope this may help.

Lane
  • 119
  • 1
  • 5
5

For Swift 3+

Simply use UITableViewStyleGrouped and set the footer's height to zero with the following:

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • 1
    You can return 0 instead of `.leastNormalMagnitude`. Also you need to implement `tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?` – Ilias Karim Feb 24 '20 at 19:06
  • @IliasKarim Setting it to 0 casues it to have a non-zero height. Also, it's not necessary to implement the `viewForFooterInSection` function in this case. – Tamás Sengel Feb 24 '20 at 20:38
  • `0` and `.leastNormalMagnitude` had the same effect. Implementing the `viewForFooterSection` method is necessary. You should return nil. This is on Swift 5.1 with Xcode 11.3.1. – Ilias Karim Feb 24 '20 at 20:52
4

The easiest way to get what you want is set your table style as UITableViewStyleGrouped, separator style as UITableViewCellSeparatorStyleNone:

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN; // return 0.01f; would work same 
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return [[UIView alloc] initWithFrame:CGRectZero];
}

Do not try return footer view as nil, don't forget set header height and header view, after you must get what you desired.

4

Although this may not solve your problem, it did for me when I wanted to do a similar thing. Instead of setting the header I used the footer of the section above. What saved me was that this section was small and static in nature, so it never scrolled below the bottom of the view.

Torbjörn
  • 41
  • 1
  • 1
3

Maybe you could use scrollViewDidScroll on the tableView and change the contentInset based on the current visible header.

It seems to work for my use case!

extension ViewController : UIScrollViewDelegate{

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard   let tableView = scrollView as? UITableView,
            let visible = tableView.indexPathsForVisibleRows,
            let first = visible.first else {
            return
        }

        let headerHeight = tableView.rectForHeader(inSection: first.section).size.height
        let offset =  max(min(0, -tableView.contentOffset.y), -headerHeight)
        self.tableView.contentInset = UIEdgeInsetsMake(offset, 0, -offset, 0)
   }
}
Martin
  • 191
  • 2
  • 4
2

While thinking how to approach this problem, I remembered a very important detail about UITableViewStyleGrouped. The way UITableView implements the grouped style (the rounded borders around the cells) is by adding a custom backgroundView to the UITableViewCells, and not to the UITableView. Each cell is added a backgroundView according to its position in the section (upper rows get the upper part of the section border, middle ones get the side border and the bottom one gets – well, the bottom part). So, if we just want a plain style, and we don’t have a custom backgroundView for our cells (which is the case in 90% of the times), then all we need to do is use UITableViewStyleGrouped, and remove the custom background. This can be done by following those two steps:

Change our tableView style to UITableViewStyleGrouped Add the following line to cellForRow, just before we return the cell:

cell.backgroundView=[[[UIView alloc] initWithFrame:cell.bounds] autorelease];

And that’s it. The tableView style will become exactly like UITableViewStylePlain, except for the floating headers.

Hope this helps!

James Laurenstin
  • 506
  • 1
  • 6
  • 13
1

To remove the floating section header sections completely, you can do this:

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [[UIView alloc] init];
}

return nil doesn't work.

To disable floating but still show section headers you can provide a custom view with its own behaviours.

djskinner
  • 8,035
  • 4
  • 49
  • 72
1

Another way to do it is to make an empty section right before the one you want the header on and put your header on that section. Because the section is empty the header will scroll immediately.

devguydavid
  • 3,917
  • 1
  • 19
  • 18
1

You can add one Section(with zero rows) above, then set the above sectionFooterView as current section's headerView, footerView doesn't float. Hope it gives a help.

daisy
  • 22,498
  • 29
  • 129
  • 265
Lee Lam
  • 91
  • 1
  • 4
0

Check the answer how to implement headers with StoryBoard: Table Header Views in StoryBoards

Also notice that if you don't implement

viewForHeaderInSection:(NSInteger)section

it will not float which is exactly what you want.

Community
  • 1
  • 1
Raphael Oliveira
  • 7,751
  • 5
  • 47
  • 55
0

Ignore XAK. Do not explore any private methods if you want your app to have the chance of being accepted by apple.

This is easiest if you are using Interface Builder. You would add a UIView at the top of the view (where the images will go), then add your tableview below that. IB should size it accordingly; that is, the top of the tableview touches the bottom of the UIView you've just added and it's height covers the rest of the screen.

The thinking here is that if that UIView is not actually part of the table view, it will not scroll with the tableview. i.e. ignore the tableview header.

If you're not using interface builder, it's a little more complicated because you've got to get the positioning and height correct for the tableview.

imnk
  • 4,342
  • 3
  • 29
  • 31
  • This doesn't answer the question. Tricky is referring to the titleHeaderViews which are the views that sit between the cells, not the table header view which is what you seem to be suggesting. – Daniel Wood Jun 15 '10 at 13:52
0

A variation on @samvermette's solution:

/// Allows for disabling scrolling headers in plain-styled tableviews
extension UITableView {

    static let shouldScrollSectionHeadersDummyViewHeight = CGFloat(40)

    var shouldScrollSectionHeaders: Bool {
        set {
            if newValue {
                tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: bounds.size.width, height: UITableView.shouldScrollSectionHeadersDummyViewHeight))
                contentInset = UIEdgeInsets(top: -UITableView.shouldScrollSectionHeadersDummyViewHeight, left: 0, bottom: 0, right: 0)
            } else {
                tableHeaderView = nil
                contentInset = .zero
            }
        }

        get {
            return tableHeaderView != nil && contentInset.top == UITableView.shouldScrollSectionHeadersDummyViewHeight
        }
    }

}
raf
  • 2,569
  • 20
  • 19
0

The snippet display a sticky header only for the first section. Others section headers will float with a cells.

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {

    if section == 1 {
        tableView.contentInset = .zero
    }

}

func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
    if section == 0 {
        tableView.contentInset = .init(top: -tableView.sectionHeaderHeight, left: 0, bottom: 0, right: 0)
    }
}
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
0

A tricky way is add an empty section for header. Because section has no cell, it will not floating at all.

Will
  • 91
  • 1
  • 5
0

**Swift 5.3 | Programmatically **

private func buildTableView() -> UITableView {
    let tableView = UITableView()
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.rowHeight = UITableView.automaticDimension
    tableView.showsVerticalScrollIndicator = false
    tableView.separatorStyle = .none
    let dummyViewHeight: CGFloat = 80
    tableView.tableFooterView = UIView(
        frame: CGRect(x: .zero,
                      y: .zero,
                      width: tableView.bounds.size.width,
                      height: dummyViewHeight))

    tableView.contentInset = UIEdgeInsets(top: .zero, left: .zero, bottom: -dummyViewHeight, right: .zero)
    return tableView
}
MaatheusGois
  • 139
  • 5
0

Best solution for below 2 scenarios:

Scenario 1: If the tableView style is not .grouped. Have no problem with changing it to grouped. footer wont float.

Scenario 2: If you want only one footer at the end of a tableView. and It's style is .plain

Steps:

  1. add one more section at the end.
  2. make sure in added section have (nuberOfRowsInSection method) zero number of rows.
  3. add custom footer to that section.
  4. set heightForFooterInSection to your custom footer and set .zero to other sections' footers.

example :

func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {

//note: Filter the section where you intended to add custom section

    let footerView = UIView()
    submitOrganiser?.showFooterAtTheEnd(view: footerView) //my method to customise the footer, use your implementation
    return footerView
}
Santosh
  • 1,275
  • 1
  • 10
  • 21
0

If you don't mind using a UICollectionView, this can also be achieved using UICollectionView list configurations in iOS 13.0+ and using headerMode set to .firstItemInSection.

override func viewDidLoad() {
    super.viewDidLoad()
    var config = UICollectionLayoutListConfiguration.init(appearance: .plain)
    config.headerMode = .firstItemInSection
    let listLayout = UICollectionViewCompositionalLayout.list(using: config)
    self.collectionView.collectionViewLayout = listLayout
}
MH175
  • 2,234
  • 1
  • 19
  • 35
-1

This can be achieved by assigning the header view manually in the UITableViewController's viewDidLoad method instead of using the delegate's viewForHeaderInSection and heightForHeaderInSection. For example in your subclass of UITableViewController, you can do something like this:

- (void)viewDidLoad {
    [super viewDidLoad];

    UILabel *headerView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 40)];
    [headerView setBackgroundColor:[UIColor magentaColor]];
    [headerView setTextAlignment:NSTextAlignmentCenter];
    [headerView setText:@"Hello World"];
    [[self tableView] setTableHeaderView:headerView];
}

The header view will then disappear when the user scrolls. I don't know why this works like this, but it seems to achieve what you're looking to do.

Johnny Oshika
  • 54,741
  • 40
  • 181
  • 275
-1

Maybe you can simply make header view background transparent:

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    view.tintColor = [UIColor clearColor];
}

Or apply it globally:

    [UITableViewHeaderFooterView appearance].tintColor = [UIColor clearColor];
Tinpont
  • 41
  • 4
-2

I have another even simpler solution, to be used without autolayout and with everything done through the XIB :

1/ Put your header in the tableview by drag and dropping it directly on the tableview.

2/ In the Size Inspector of the newly made header, just change its autosizing : you should only leave the top, left and right anchors, plus the fill horizontally.

That's how it should be set for the header

That should do the trick !

CyberDandy
  • 470
  • 6
  • 17
-2

According to @samvermette's answer,I've implemented the above code in Swift to make it easy for coders to use swift.

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0)
Bingerz
  • 1,027
  • 1
  • 11
  • 15
-2

ref: https://stackoverflow.com/a/26306212

let tableView = UITableView(frame: .zero, style: .grouped)

PLUSE

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if section == 0 {
        return 40
    }else{
        tableView.sectionHeaderHeight = 0
    }

    return 0
}

This helped to use header space

Pratik
  • 676
  • 10
  • 9
-4

you can easily achieve this by implementing the viewForHeaderInSection method in the tableview delegate class. this method expects a UIView as return object (which is your header view). i have done this same thing in my code

Raj
  • 6,810
  • 6
  • 48
  • 56
  • The original poster doesn't want a custom header. They want a header that doesn't "float" at the top of the table. Your suggestion still floats. – jemmons Jul 19 '10 at 00:05
  • change the tableview to UITableViewStyleGrouped with viewforheader, then it will make it stop – mskw Apr 22 '14 at 01:56