I've got a UITableView with one section and enough rows that the tableView needs to be scrolled to get to the bottom. I want to add a footer view which will stick to the bottom of the tableView and always be visible, so I have implemented viewForFooterInSection. Here's my code:
- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
NSLog(#"Get footer view");
if (tableView == [self tableView]) {
return [self footerRowRightView];
}
else if (tableView == [self fixedColumnTableView]) {
return [self footerRowLeftView];
}
return nil;
}
The problem I am having is that the footer view only shows after the tableView has been scrolled, but I want it to be visible from the outset (i.e. always floating whether the user scrolls or not).
As soon as the controller appears and the tableView loads its data, I see "Get footer view" in the log, so I know that viewForFooterInSection is being called straight away. What I can't work out is why it doesn't display immediately, and how to get it to do so.
Thanks in advance for any help!
It is probably your height for the footer not being returned correctly.
Check what you return from heightForFooterInSection
What you need is not a tableview's footer.
Simply add the view corresponding to this header in the superview of your tableview and put it at the bottom of it. Then simply reduce the height of the frame of your tableview to fit the remaining space. And it should do it !
You can no use footer view if you want to stick the footer. Or try with grouped tableview.
Quite a few options by the looks of the other answers. Just to add a hacky workaround I have just come up with, I created duplicates of the views I will be using as footers and added them as subviews of my main view, placed exactly over the position of where the real footer views. The views are retained in properties, so that in scrollviewDidScroll I can do the following:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([self preScrollFooterLeftView]) {
[[self preScrollFooterLeftView] removeFromSuperview];
}
if ([self preScrollFooterRightView]) {
[[self preScrollFooterRightView] removeFromSuperview];
}
}
This way the footer appears to be displayed immediately. The fake footer is removed as soon as the user scrolls the tableView, revealing the real footer beneath it. If the tableView is scrolled below the last row, the real header sticks to the bottom of the section and bounces back to the bottom of the tableView when the user lets go.
Related
I have a UITableView configured as plain style so I can have the header views stuck on the top of the table until another header pulls it away.
The problem is: If I have a header stuck on the top of the screen, and I programmatically scroll to another part of the table (where that header should not appear at all), that UIView will not be dismissed. I.e. if I scroll again to that part of the table, a ghost of that header will be visible on that part of the table.
I've implemented the method - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(nonnull UIView *)view forSection:(NSInteger)section to understand what is happening. I found that if I manually scroll until a header is pull away of the screen, this delegate is called. But if I scroll programmatically, the delegate is not called.
By the way, I tried scrolling programmatically using two different methods, and the problem is the same.
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;
One workaround that I can imagine is implementing - (void)scrollViewDidScroll:(UIScrollView *)scrollView;, filtering all the header views that are outside the visible screen, and removing them from superview. I can probably make it work, but I would like to know if there is any other better solution.
[EDIT] If I call - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; with animated = YES, the bug does not happen. I can go with this solution, but I really would like in some cases to scroll without animation.
Not entirely sure I understand your issue entirely but it seems that your header view(s) (some UIView) is/are not rendered correctly once you programmatically scroll away from this area / section and then return.
I'm not sure how you are filling your header view content but I have several applications running UITableView's with multiple section headers that require updating for scrolling / content offset's with no problem, as long as you "draw" your headers with this delegate:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
// Per section, simply return the appropriate header view
...
NSString *someIdentifier = [NSString stringWithFormat:#"sectionHeaderView:<some #, letter, or tag>", <SOMETHING UNIQUE ADD HERE>];
UITableViewHeaderFooterView *myHeaderView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:someIdentifier];
if (!myHeaderView) {
// No header view found with that ID. Make a new one
}
...
return myHeaderViewForSection;
}
This way whether you finger scroll or programmatically set the content offset which ever way you like, your table view will know what to draw, when to draw it, and where to put it.
Using their delegates is a bit of a drag as it's slightly tedious at start, but using the viewForHeaderInSection proved the only way I ever obtained the results I (you) wanted.
Hope this helps - happy coding!
TL;DR
Do NOT explicitly scroll the table view between beginUpdates and endUpdates.
Explanation
I'm using NSFetchedResultsController to populate the table. These are my implementations for some of the methods of NSFetchedResultsControllerDelegate.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[_conversationTableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[_conversationTableView endUpdates];
}
The problem is that endUpdates was making a chain of calls that ended calling my method [self scrollToBottom] (which was a very ugly code actually). This method, as the name says, calls - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; to scroll the table view to the bottom of the table.
The explicit scrolling of the table during a beginUpdates - endUpdates was the culprit of my whole problem.
Solution
Scrolling the table view only after finishing endUpdates.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[_conversationTableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[_conversationTableView endUpdates];
[self scrollToBottom];
}
Side Note
This also fixed a problem where the table view was sometimes flickering when scrolling.
Manually set the sectionHeader height to 0 when it should not appear
tableView.sectionHeaderHeight = 0;
In my app I need to design the view with header, content view (table view) and footer view which is scrollable. Content view data will change dynamically. So I have used table view.
I have added the Header and footer view in the table view header and footer. (My goal is to scroll the header, content and footer view so the I have added my custom header and footer view in UItable view header and footer.)
My design:
It's working as per the design if the table view contains some data. Issue is, if table view doesn't contains any data (row count simply 0). The header view is display in the middle of the view (Table view frame shrink automatically in this case). Even I tried to handle the table view frame based on the data source count. But I can't.
How can I fix this issue?
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
You can use this code to customize footer height of UItableview
by putting valid if else
In a UItableView,The footer always appears at the end of section .But here you don't have any existance of cells and you want to show the footer at the bottom of the page .
Solution:
Take one cell before getting data and set the height for that cell
BOOL hasData;//Initialise in ViewDidLoad and set value according to data
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (!hasdata) {
return 1;
}else{
return dataArray.count;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if (!hasData) {
cellHeight =
self.view.frame.size.height -
(tableViewHeaderHeight+tableViewFooterHeight);
}else{
//Set row height
}
}
and then after getting data for the tableview again reload that .
Hope this help you.Try this .It worked for me.
Table view sections have a header view and a footer view. Between those are the cells for that particular section. It sounds like you want to simulate the existence of cells that don't really exist.
Perhaps what you really want is a standard UIView as your header and footer views with a UITableView in between. The UITableView won't change size based on its content and will scroll when independently of the header and footer views.
I have checked all these
UITableView, make footer stay at bottom of screen?
tableFooterView property doesn't fix the footer at the bottom of the table view
iOS - viewForFooterInSection sticking to bottom of UITableView
Proper way to implement a footer in UITableView
similar questions but unfortunately my problem hasn't resolved.
I have to implement a custom header and footer views with buttons inside. I have created separate UIView's subclasses with .nib files. In my view controller, I'm calling these methods to register nibs for header and footer view.
- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CustomTableHeaderView *view = [CustomTableHeaderView header];
view.delegate = self; //setting delegate to receive callbacks as the buttons inside the view are pressed
return view;
}
- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
CustomTableFooterView *view = [CustomTableFooterView footer];
view.delegate = self;
return view;
}
Where as the class method in the custom views registers a .nib file and returns the view. However the implementation is;
+ (CustomTableHeaderView*)header
{
return [[[NSBundle mainBundle]loadNibNamed:#"CustomTableHeaderView" owner:nil options:nil]objectAtIndex:0];
}
Similar implementation for footer.
The problem is that the footer view doesn't lock at the bottom when the table view scrolls. i-e, when there are more rows to fit inside the view, the footer view hides and is revealed when all the rows are scrolled down till the end. I want to lock the footer view at the bottom of the view no matter how much rows are there to scroll.
The header view has been implemented perfectly by this implementation as it is locked at the top while the rows are being scrolled, however the footer view is scrolled with the rows.
I have also tried self.tableview.tablefooterview property but it didn't help either.
Unfortunately thats not how table section footers work. In order to accomplish an anchored view at the bottom you will need to add it as a subview to your UIView manually.
If you add it as a subview to your UITableView you will need to keep it anchored by changing its frame in scrollViewDidSroll:. If you add it as a subview to the UIView containing your UITableView you can just place it statically at the bottom. In either case you probably want to adjust the contentInset of the table view with an inset at the bottom so that you can scroll your content up above the anchored footer.
When I page to the right using UIScrollview within my UITableViewCell, it also scrolls in every fourth cell from that cell, up and down.
It is creating the scroll view based on a property within the containing view, contained by a UITableViewCell.
the only method the scrollview has is
-(void)scrollViewDidEndDecelerating(UIScollView *)scrollView{
scrollview.contentoffset = CGPointMake(scrollView.bounds.size.width,0);
}
Basically every time a new cell is drawn, it takes the scroll position of the cell that has just been undrawn.
Is there any work around for this besides disabling deque? I mean, I'd like to be able to scroll down and then scroll back up without affecting the horizontal scroll within the cell.
Thanks for the help
Sounds like your cells, which are reused/dequeued, aren't being reset.
When you dequeue and configure your cells you need to include logic to set as well as reset, something like this:
if (isCustom) {
[cell setupAsCustom];
} else {
[cell setupAsDefault];
}
As opposed to just this:
if (isCustom) {
[cell setupAsCustom];
}
In this case the customness of the cell is its contentOffset. Hope this helps.
I found out, that UITableView modify contentSize property and I can't set it by myself.
This is my call stack. After this call contentSize is modified. I don't want it, because of that I want to hide header, so I need to scroll view a little bit down, and to make it I set contentSize little higher than visible to be able to scroll down a bit.
Any ideas how to hide table header, and keep it hidden after insertion/deletion ? By hide I mean table header initially "hidden" but show up when you scroll to the top.
I'm using NSFetchedResultsController to fetch data with CoreData.
You can implement the tableview delegate methods for the size of header and return 0. it would like this:
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 0;
}
This would set the header for each section to be zero. You can also edit the footer size in the same fashion with heightForFooterInSection:
Looks like contentSize is modified by iOS private code just after controllerDidChangeContent is called.
Just for record. This is my solution (searchView is view that I want to hide):
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentSize.height <= scrollView.frame.size.height)
{
[scrollView setContentOffset:CGPointMake(0, self.searchView.frame.size.height)];
[scrollView setContentSize:CGSizeMake(scrollView.contentSize.width, tableView.frame.size.height + self.searchView.frame.size.height)];
}
}