ChatGPT解决这个技术问题 Extra ChatGPT

Change Default Scrolling Behavior of UITableView Section Header

I have a UITableView with two sections. It is a simple table view. I am using viewForHeaderInSection to create custom views for these headers. So far, so good.

The default scrolling behavior is that when a section is encountered, the section header stays anchored below the Nav bar, until the next section scrolls into view.

My question is this: can I change the default behavior so that the section headers do NOT stay anchored at the top, but rather, scroll under the nav bar with the rest of the section rows?

Am I missing something obvious?

Thanks.

That must be one of the best written questions on this site! :) Thanks.
Check here for the right way to achieve this effect stackoverflow.com/a/13735238/1880899

F
Fattie

The way I solved this problem is to adjust the contentOffset according to the contentInset in the UITableViewControllerDelegate (extends UIScrollViewDelegate) like this:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat sectionHeaderHeight = 40;
   if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
       scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
   } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
       scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
   }
}

Only problem here is that it looses a little bit of bounce when scrolling back to the top.

{NOTE: The "40" should be the exact height of YOUR section 0 header. If you use a number that is bigger than your section 0 header height, you'll see that finger-feel is affected (try like "1000" and you'll see the bounce behaviour is sort of weird at the top). if the number matches your section 0 header height, finger feel seems to be either perfect or near-perfect.}


Perfect solution than the above one.
Thank you! This is a perfect solution. Do you have the solution for the section footer?
This solution does not handle tap-on-menubar correctly, which is supposed to scroll to the top of the list.
if u need the same behaviour for section footer view just change the last line like this:scrollView.contentInset = UIEdgeInsetsMake(0, 0, sectionHeaderHeight, 0)
This seems to be enough: scrollView.contentInset = UIEdgeInsetsMake(scrollView.contentOffset.y >= 0 ? -scrollView.contentOffset.y : 0, 0, 0, 0);
v
voidStern

You can also add a section with zero rows at the top and simply use the footer of the previous section as a header for the next.


In my case where I have more than 2 sections, the footer will anchors at the bottom..
This is creative but you need to do adjust the indexPath if you're using an NSFetchedResultsController to load the table.
+1 by far the BEST solution - took me like 3 lines to implement!
Brilliant Brilliant Brilliant :-)
How would this work for footer that anchors? Would appreciate some help! Thanks
C
Colin Barrett

Were it me doing this, I'd take advantage of the fact that UITableViews in the Plain style have the sticky headers and ones in the Grouped style do not. I'd probably at least try using a custom table cell to mimic the appearance of Plain cells in a Grouped table.

I haven't actually tried this so it may not work, but that's what I'd suggest doing.


Probably would work, but has it been tried? And it seems like a lot of work when @voidStern's answer works perfectly!!
voidStern's answer doesn't work for me when I have more than two sections. Colin's answer is simpler/more elegant, without the need to manually shift section number and add section count.
Tips: use tableView.separatorColor = [UIColor clearColor]; to mimic a plain table view.
I tried doing this, but when I couldn't make the grouped table cells look like the plain ones, i.e. remove the left and right margin either side of the cells. How did you do this?
Thanks a lot for making things clear about UITableViewStyle i.e grouped and plain, which decides whether tableview's section header/footer will be sticky or not. Thanks much @Colin Barrett
L
LocoMike

I know it comes late, but I have found the definitive solution!

What you want to do is if you have 10 sections, let the dataSource return 20. Use even numbers for section headers, and odd numbers for section content. something like this

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }else {
        return 5;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        return [NSString stringWithFormat:@"%i", section+1];
    }else {
        return nil;
    }
}

Voilá! :D


great idea. but if you have section index titles, I suspect this will mess them up.
why? you can do the same with section titles. Just remember nil for odd numbers and a title for the even number.
yes - this method works great. It was a lot of bookkeeping (probably to do with the way i have to get the section titles) But it worked. The trick is that numberOfRowsInSection and cellForRowAtIndexPath uses if (section%2!=0 && section!=0) whereas the titleForHeaderInSection and the other section index functions use if (section%2==0)
the originator should choose this as the answer. It'd be great if there was a way to subclass uitableview to anchor the section titles automatically.
This worked the best for me. I tried @Colin's solution first, but it didn't work well with the heavily customized UITableView that I'm using.
C
Community

Originally posted Here, a quick solution using the IB. The same can be done programmatically though quite simply.

A probably easier way to achieve this (using IB): Drag a UIView onto your TableView to make it its header view. Set that header view height to 100px Set the tableview contentInset (top) to -100 Section headers will now scroll just like any regular cell.

Some people commented saying that this solution hides the first header, however I have not noticed any such issue. It worked perfectly for me and was by far the simplest solution that I've seen so far.


Note that you should probably set the view to use a background color of clearcolor, otherwise if you scroll past the top you see white in the bounce.
It does hides my first header
Leena, what version of iOS are you running on? Is the view a UITableViewController, or a UIView with a UITableView in it? I'm not sure why for some people this solution hides the first header so I'm trying to find any discrepancies.
This solution is perfect. I'm looking for an infinite top and bottom cell background appearance - this totally accomplishes it. Nice!
This solution is excellent. Easier to accomplish than the other answers in this thread. In my opinion, this should be the accepted answer.
N
Neil Gall

There are several things that need done to solve this problem in a non-hacky manner:

Set the table view style to UITableViewStyleGrouped Set the table view backgroundColor to [UIColor clearColor] Set the backgroundView on each table view cell to an empty view with backgroundColor [UIColor clearColor] If necessary, set the table view rowHeight appropriately, or override tableView:heightForRowAtIndexPath: if individual rows have different heights.


(+1) @Neil, I tried your answer but unfortunately, for UITableViewStyleGrouped the table cells have extra margins that change their alignment with the header. This is a problem when you actually want to depict a multi-column table and use the header to show column titles (and then have the individual table entries align with these titles).
T
Thomas Kekeisen

I was not happy with the solutions described here so far, so I tried to combine them. The result is the following code, inspired by @awulf and @cescofry. It works for me because I have no real table view header. If you already have a table view header, you may have to adjust the height.

// Set the edge inset
self.tableView.contentInset = UIEdgeInsetsMake(-23.0f, 0, 0, 0);

// Add a transparent UIView with the height of the section header (ARC enabled)
[self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 23.0f)]];

This was a simplest solution I found.
This worked for me on iOS9.2, so even though an older answer, it is still valid. And it didn't hide the first section header. Works, and works perfectly.
Perfect. If your tableview already has a header, just increase its height by the offset then increase the header content's top constraint constant to shift the content down to the original position
A
Arthur S

Just change TableView Style:

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

UITableViewStyle Documentation:

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.


great answer, worked for me. no need to any work around
A
Arjun Shukla

https://i.stack.imgur.com/168As.png

Select Grouped Table View style from your tableView's Attribute Inspector in storyboard.


b
beggs

Set the headerView of the table with a transparent view with the same height of the header in section view. Also initi the tableview with a y frame at -height.

self.tableview = [[UITableView alloc] initWithFrame:CGRectMake(0, - height, 300, 400)];

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)] autorelease];
[self.tableView setTableHeaderView:headerView];

A
Ash

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. Hope this small change will help you ..


A
AlexBB

I found an alternative solution, use the first cell of each section instead a real header section, this solution don't appears so clean, but works so fine, you can use a defined prototype cell for your headers section, and in the method cellForRowAtIndexPath ask for the indexPath.row==0, if true, use the header section prototype cell, else use your default prototype cell.


If you still want to work with indexPath.row & indexPath.section, make sure you return a height of 0.0f for - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section so your headers are invisible.
G
Greg Combs

Now that the grouped style looks basically the same as the plain style in iOS 7 (in terms of flatness and background), for us the best and easiest (i.e. least hacky) fix was to simply change the table view's style to grouped. Jacking with contentInsets was always a problem when we integrated a scroll-away nav bar at the top. With a grouped table view style, it looks exactly the same (with our cells) and the section headers stay fixed. No scrolling weirdness.


This is the best solution especially if the cells are custom, it seems to simply stop the "hold" of the section header when the scroll hits the top.
M
Mike A

Assign a negative inset to your tableView. If you have 22px high section headers, and you don't want them to be sticky, right after you reloadData add:

self.tableView.contentInset = UIEdgeInsetsMake(-22, 0, 0, 0); 
self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+22); 

Works like a charm for me. Works for section footers as well, just assign the negative inset on the bottom instead.


This just chops off the first header??
Worked for me perfectly!! Thanks
D
Dolbex

I add the table to a Scroll View and that seems to work well.


C
Community

Check my answer here. This is the easiest way to implement the non-floating section headers without any hacks.


m
marmor

@LocoMike's answer best fitted my tableView, however it broke when using footers as well. So, this is the corrected solution when using headers and footers:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return (self.sections.count + 1) * 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section % 3 != 1) {
        return 0;
    }
    section = section / 3;
    ...
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

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

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return 0;
    }
    section = section / 3;
    ...
}

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

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    int section = indexPath.section;
    section = section / 3;
    ...
}

J
Jeff

Swift version of @awulf answer, which works great!

func scrollViewDidScroll(scrollView: UIScrollView) {
    let sectionHeight: CGFloat = 80
    if scrollView.contentOffset.y <= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake( -scrollView.contentOffset.y, 0, 0, 0)
    }else if scrollView.contentOffset.y >= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
    }
}

S
Soni

I've learned that just setting the tableHeaderView property does it, i.e. :

 tableView.tableHeaderView = customView;

and that's it.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now