Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView - How to keep table rows fixed as user scrolls

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

like image 807
tarmes Avatar asked Jun 05 '12 08:06

tarmes


2 Answers

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...

like image 73
tarmes Avatar answered Sep 29 '22 16:09

tarmes


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.

like image 37
Fran Sevillano Avatar answered Sep 29 '22 16:09

Fran Sevillano