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
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...
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.
Swift 5 solution
var header: UIView?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as UITableViewCell
header = cell.contentView
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let headerCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0))
guard headerCell == nil || (headerCell!.frame.origin.y < self.tableView.contentOffset.y + headerCell!.frame.height/2) else {
header?.isHidden = true
return
}
guard let hdr = header else { return }
hdr.isHidden = false
hdr.frame = CGRect(x: 0, y: tableView.contentOffset.y, width: hdr.frame.size.width, height: hdr.frame.size.height)
if !tableView.subviews.contains(hdr) {
tableView.addSubview(hdr)
}
tableView.bringSubviewToFront(hdr)
}