I have a UITableView and a navigation bar in a single screen of my app. I want to navigation bar to be scrolled together with the rest of the table.
Note: I am currently implementing a UITableView that is added as a subview of a uiviewcontroller which is in turn part of a navigation controller
Can anyone advise me on how to do this?
In the following answer I'm hiding the real nav bar and replacing it with a fake bar. If you want to use the real nav bar instead, the code is still valid, just restore the original position of the bar in viewWillDisappear.
Hide the navigation controller bar:
self.navigationController.navigationBar.hidden = YES;
Add a top bar as a subview of the current controller view.
UIView *headerView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bar.png"]];
[self.view addSubView:headerView];
At this point you have a headerView and a tableView below. Save their original frames:
@property (nonatomic,assign) CGRect initialTableRect;
@property (nonatomic,assign) CGRect initialHeaderRect;
// This is how many pixels you are going to scroll up under the clock bar.
// To scroll your whole headerView, set it to self.headerView.frame.height
@property (nonatomic,assign) NSInteger kHiddenPixels;
// viewDidLoad
self.initialTableRect = self.tableView.frame;
self.initialHeaderRect = self.headerView.frame;
self.kHiddenPixels = self.headerView.frame.height;
Add this in the view controller, which is also your table delegate:
#pragma mark UIScrollViewDelegateMethods
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
// 0 at the initial position, negative down, positive up
CGPoint offset = aScrollView.contentOffset;
if (offset.y>=0 && offset.y<=kHiddenPixels)
{
// move header
CGRect newRect = self.initialHeaderRect;
newRect.origin.y -= offset.y;
self.headerView.frame = newRect;
// move and resize table
newRect = self.initialTableRect;
newRect.origin.y -= offset.y;
newRect.size.height += offset.y;
self.tableView.frame = newRect;
}
else if (self.headerView.frame.origin.y!=0 || self.headerView.frame.origin.y!=kHiddenPixels)
{
// outside the scrolling area but frame is not on 0 or kHiddenPixels,
// which means we skipped scroll because drag was to fast
if (offset.y<0){
// set initial position on header and table
self.headerView.frame = self.initialHeaderRect;
self.tableView.frame = self.initialTableRect;
}
else if (offset.y>kHiddenPixels)
{
// set scrolled up position on header
CGRect rect = self.initialHeaderRect;
rect.origin.y = -1*kHiddenPixels;
self.headerView.frame = rect;
// set scrolled up position on table
rect = self.initialTableRect;
rect.size.height += kHiddenPixels;
rect.origin.y -= kHiddenPixels;
self.tableView.frame = rect;
}
}
}
This checks the table height and moves top bar as the table view scrolls. Since the tableView is moving over the are previously filled by the top bar, I'm also enlarging the size of the table view.
edit: just uploaded an implementation in github with a top bar of 80 pixels and kHiddenPixels=35:
A possible enhancement: Right now, as the first row of the tableView scrolls under the top bar, the top bar scrolls below the clock bar. It would look more natural if the tableView and top bar scroll together until the top bar is gone, and then let the 1st row start scrolling under the top bar. Not sure how to do this, maybe with a pan gesture recognizer and disabling table scroll until the bar is hidden.
The only way a navigation bar can be scrolled with the other cells of table-view is if the navigation bar is a part of the table view or is a subview of a larger scroll-view (in addition to the table-view's subview). check out this question: How can I make the navigation bar scroll along with my webpage?. Make a larger scroll-view and add the table-view and navigation-bar to this scroll-view. If you want to make the navigation bar a part of the table-view, you'll need to make a custom-cell for it, which will incur a lot more hassle!