UITableView - How to keep table rows fixed as user scrolls

12,009

Solution 1

I've been playing about with this and I've come up with a simple solution.

First, we add a single UITableViewCell property to the controller. This should be initialize such that looks exactly like the row cells that we'll use to create the false section headers.

Next, we intercept scrolling of the table view

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // Add some logic here to determine the section header. For example, use 
    // indexPathsForVisibleRows to get the visible index paths, from which you 
    // should be able to get the table view row that corresponds to the current 
    // section header. How this works will be implementation dependent.
    //
    // If the current section header has changed since the pervious scroll request 
    // (because a new one should now be at the top of the screen) then you should
    // update the contents.

    IndexPath *indexPathOfCurrentHeaderCell = ... // Depends on implementation
    UITableViewCell *headerCell = [self.tableView cellForRowAtIndexPath:indexPathOfCurrentHeaderCell];

    // If it exists then it's on screen. Hide our false header

    if (headerCell)
        self.cellHeader.hidden = true;

    // If it doesn't exist (not on screen) or if it's partially scrolled off the top,
    // position our false header at the top of the screen

    if (!headerCell || headerCell.frame.origin.y < self.tableView.contentOffset.y )
    {
        self.cellHeader.hidden = NO;
        self.cellHeader.frame = CGRectMake(0, self.tableView.contentOffset.y, self.cellHeader.frame.size.width, self.cellHeader.frame.size.height);
    }

    // Make sure it's on top of all other cells

    [self.tableView bringSubviewToFront:self.cellHeader];
}

Finally, we need to intercept actions on that cell and do the right thing...

Solution 2

That's the default behavior for section headers in plain UITableView instances. If you want to create a custom header, implement the tableView:viewForHeaderInSection: method in your table view delegate and return the view for your header.

Although you will have to manage sections and rows instead of just rows.

Share:
12,009

Related videos on Youtube

tarmes
Author by

tarmes

I've been a software engineer since 1994, working in various fields from compiler design to embedded systems for ink jet printers. Since 2010 I've been specialising in developing full-stack web applications and software for iOS (iPhone, iPad, iPod) and Mac. My own iOS applications are commercialiapplisoftwaresed via my development company Arctic Whiteness. I'm also known for my plugins for Adobe Lightroom. I'm passionate about great software design. Applications should be easy to use, capable and beautiful. Internally software architecture should have clear separation and purpose and be easy to maintain, extend and test. On a personal level, just like everyone else I'm a complex combination of many different abilities and interests. I'm a Brit living in the South of France, a freelance software developer, a husband, a dad of two, a passionate sailor, a photographer, a go player, a climber and a cook. I enjoy technology, astronomy, puzzle solving, teaching, travel, the mountains, the sea, music, working for myself and lots of other things besides. I am available for hire - please contact me to discuss any development work.

Updated on October 01, 2022

Comments

  • tarmes
    tarmes over 1 year

    I'd like to be able to fix the position of certain rows in a UITableView as the user scrolls.

    Specifically, I have a table whereby certain rows are "headers" for the rows that follow, and I'd like the header to stay at the top of the screen as the user scrolls up. It would then move out of the way when the user scrolls far enough that the next header row would take its place.

    A similar example would be the Any.DO app. The "Today", "Tommorrow" and "Later" table rows are always visible on the screen.

    Does anyone have any suggestions about how this could be implemented?

    I'm currently thinking of follow the TableDidScroll delegate and positioning my own cell in the appropriate place in front of the table view. The problem is that at other times I'd really like these cells to be real table cells so that they can be, for example, reordered by the user.

    Thanks,

    Tim

    • Novarg
      Novarg almost 12 years
      why don't you use UITableView headers? You can put text there or make a custom view. There are methodes of UITableViewDelegate for that
  • tarmes
    tarmes almost 12 years
    The problem is that I'd like to be able to recorder the section headers. As far as I can tell there's no UI that'll allow the user to do that in the same way as they can currently drag section rows about...
  • Fran Sevillano
    Fran Sevillano almost 12 years
    I see, you will have to handle that yourself. Seems a little complicated but I am sure it can be done. It seems like there is nothing implemented out there. Maybe I can help you with it and we can put it on Github or something.
  • tarmes
    tarmes almost 12 years
    Interestingly, there is now a moveSection:toSection: call in iOS5, but I don't think there's a way to initiate this move from the UI...
  • Fran Sevillano
    Fran Sevillano almost 12 years
    You can add a button or a gesture recognizer to the header view. That way you can get when the section is selected.
  • Hai
    Hai almost 10 years
    Thanks a lot, tarmes. I am looking for the similar thing and need also implement your logic in my application. However, i could not get what is the headerCell you mean? And when you mean the headerCell is on the screen?
  • tarmes
    tarmes almost 10 years
    The headerCell is the cell in the table view that you wish to stay at the top of the table as it's scrolled.
  • User
    User over 8 years
    Question: You say you use this to be able to reorder the headers. Do you collapse the content cells to make the header reordering easier? I was thinking about it but not sure if it's possible, as reloadData or other type of table view update would probably interrupt /revert the floating header cell.
  • User
    User over 8 years
    To be clear - just in case - I mean when user taps on the handler to start dragging the header, in this moment I would like to "close" the sections, so only headers are visible, and it's easier to place the header in its new position. I would need to trigger reloadData or updates in order to collapse the sections when user starts dragging, which I think would cancel the dragging.
  • User
    User over 8 years
    If not possible alternatively could have a separate button where the user can manually collapse all the headers and then reorder... but it would be neat if this can be done automatically, when the user starts dragging.