I have a UITableView that is set to not enable scrolling, and it exists in a UIScrollView. I'm doing it this way as the design specs call for something that looks like a table view, (actually there are two of them side by side), and it would be much easier to implement tableviews rather than adding a whole bunch of buttons, (grouped table views).
Question is, I need to know how big to make the container view for the scrollview, so it scrolls the whole height of the table views. Once loaded, is there any way to find the height of a tableview? There is no contentView property like a scroll view, frame seems to be static, etc...
Any thoughts?
Use
CGRect lastRowRect= [tableView rectForRowAtIndexPath:index_path_for_your_last_row];
CGFloat contentHeight = lastRowRect.origin.y + lastRowRect.size.height;
You can then use the contentHeight variable to set the contentSize for the scrollView.
A more general solution that works for me:
CGFloat tableViewHeight(UITableView *tableView) {
NSInteger lastSection = tableView.numberOfSections - 1;
while (lastSection >= 0 && [tableView numberOfRowsInSection:lastSection] <= 0)
lastSection--;
if (lastSection < 0)
return 0;
CGRect lastFooterRect = [tableView rectForFooterInSection:lastSection];
return lastFooterRect.origin.y + lastFooterRect.size.height;
}
In addition to Andrei's solution, it accounts for empty sections and section footers.
UITableView is a subclass of UIScrollView, so it has a contentSize property that you should be able to use no problem:
CGFloat tableViewContentHeight = tableView.contentSize.height;
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width, tableViewContentHeight);
However, as several other SO questions have pointed out, when you make an update to a table view (like inserting a row), its contentSize doesn't appear to be updated immediately like it is for most other animated resizing in UIKit. In this case, you may need to resort to something like Michael Manner's answer. (Although I think it makes better sense implemented as a category on UITableView)
You can run over the sections and use the rectForSection to calculate the total height (this included footer and header as well!). In swift I use the following extension on UITableView
extension UITableView {
/**
Calculates the total height of the tableView that is required if you ware to display all the sections, rows, footers, headers...
*/
func contentHeight() -> CGFloat {
var height = CGFloat(0)
for sectionIndex in 0..<numberOfSections {
height += rectForSection(sectionIndex).size.height
}
return height
}
}
Related
Let's say I have a UITableView with dynamic content, but imagine it with one section consisting of a table header of 100px and 6 rows รก 44px.
Keep in mind that there could potentially be 1 section with 2 rows, or 4 sections with 10 rows etc., completely dynamic.
When the tableView displays its content I would not be able to scroll at all (only bounce) because all of the content in the tableView is visible to begin with.
I need to be able to scroll away the section header of 100px, giving more empty space at the bottom. Just enough space so that the header is outside, and the first cell is at the very top.
To achieve this, I could store a prototype instance of every different cell and "manually" calculate the height of the total cells, then add a phantom cell (or a section footer) with the remaining height needed. I feel like there should exist a better way though.
In cases where the collective cells heights are larger than the visible screen, then I'd like everything to be normal, no extra space at the bottom.
I tried manually setting the tableView.contentSize after reloadTable to be max(tableView.bounds.size.height+100, tableView.contentSize), but it seems like contentSize is overridden no matter what I do, so I'm trying to find other ways to do it.
This worked perfectly
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let visibleHeight = self.tableView.bounds.size.height - self.topLayoutGuide.length - self.bottomLayoutGuide.length
var actualContentSize = visibleHeight - (self.tableView.contentSize.height - (header.bounds.size.height - header.subviewThatShouldBeVisibleIfAny.bounds.size.height))
if #available(iOS 11.0, *){
//Nothing
}else{
actualContentSize += self.bottomLayoutGuide.length
}
var inset = self.tableView.contentInset
if #available(iOS 11.0, *){
inset.bottom = max(actualContentSize, 0)
}else{
inset.bottom = max(actualContentSize, self.bottomLayoutGuide.length)
}
self.tableView.contentInset = inset
}
viewDidLayoutSubviews will be invoced on its own, but I also have to call it manually whenever I update the data in the tableView.
You are looking for this. Change the content offset according to your needs.
self.tableView.contentOffset = CGPointMake(0, 100);
In my UIViewController I have dragged a UITableView which has 2 custom Prototype Cells. Basically Its a accordion.
When I am loading data on it there always a gap coming between my table view starting position and first row. when I scroll down Its working fine.I have searched in google and find couple of suggestion like
put
self.automaticallyAdjustsScrollViewInsets = YES;
In viewDidLoad OR add This below code in
UIEdgeInsets insets = UIEdgeInsetsMake(self.topLayoutGuide.length,
0.0,
self.bottomLayoutGuide.length,
0.0);
_detailsTableView.contentInset = insets;
or
YouStoryboard.storyboard > YouViewController > Attributes inspector > Uncheck - Adjust scroll view insets
But none of this worked for me. Here is my UITableView screenshot. Please suggest that how I can remove this gap marked in red.
After fixing this issue UITableView should looks like
Inspector Image 1
Try implementing this method:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
CGFloat height = UITableViewAutomaticDimension;
if (section == 0) {
height = 0.5f;
}
return height;
}
This will remove the spacing for the first section header. If you want to remove all spacings between sections you can always return 0.5f. Such an odd value is needed because Apple does not allow you to return 0 in this method. (That would have the same effect as not implementing the method at all.) But the value 0.5f will have the desired effect.
I think the space you are trying to get rid of is the header. Use the tableview delegate to set the header on section 0 to a height of 0.
tableView:heightForHeaderInSection
From the question the answer seems just reloadData.
This method starts the process that makes the tableview reload data from tableview from numberOfSections to -cellForRowAtIndexPath.
The fact is that most of those methods are launched probably in the next runloop or CATransaction.
I'm trying to get the full size of the tableview to show all the cells thus avoiding the need of scrolling.
The tableView knows its content size only when it load the heightForRowAtIndexPath or the cells, so it seems to impossible to ask the table view for its contentSize right after -reloadData.
It seems useless also to invalidate its intrinsic content size and force auto layout, or asking a -sizeToFit until it loads all the cells it doesn't know its size. Tried also with +systemLayoutSizeFittingSize:giving UILayoutFittingExpandedSize with no success.
The whole point of table and collection views is that you do not have to load all the data at once but recycle the UI elements and only load what is visible. That is the purpose of the datasource approach.
To add an item at the end, just change your data source to have one more that is returned from numberOfRowsInSection or numberOfItemsInSection. Make sure your data source also has content to populate this data point.
You can then reloadRowsAtIndexPaths:, reloadItemsAtIndexPaths: reloadSections: etc. methods of the datasource protocols to reload these data points, but if they are not visible that might not even be necessary.
If, on the other hand, you have a custom UIScrollView, your data source should have the heights or widths of your cells / subviews. You can then easily calculate the position of any new subview to be added.
If you want to get the right content size of your tableView, than you can calculate it yourself using heightForRowAtIndexPath:.
E.g. if cell's height is static, i.e. its never changes, then just simply multiply number of your rows with that height. Else, if you cell's height is dynamic, then you can do following:
- (CGSize)tableViewContentSize
{
CGFloat contentHeight = 0;
for (int i = 0; i < numberOfSections; i++) {
for (int k = 0; k < numberOfRows; k++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:k inSection:i];
contentHeight += [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
}
}
CGSize contentSize = CGSizeMake(self.tableView.frame.size.width, contentHeight);
return contentSize;
}
I have a UITableViewCell subclass which has an image, title and description.
I am supposed to resize the cell height according to the description content length i.e. if it spans more than 5 lines, I should extend it (+other subviews like image etc) till it lasts.
The next coming cells should begin only after that.
I have my UITableViewCell subclass instantiated from xib which has a fixed row height = 160.
I know this is pretty standard requirement but I am unable to find any guidelines.
I already extended layoutSubViews like this:
- (void) layoutSubviews
{
[self resizeCellImage];
}
- (void) resizeCellImage
{
CGRect descriptionRect = self.cellDescriptionLabel.frame;
CGRect imageRect = self.cellImageView.frame;
float descriptionBottomEdgeY = descriptionRect.origin.y + descriptionRect.size.height;
float imageBottomEdgeY = imageRect.origin.y + imageRect.size.height;
if (imageBottomEdgeY >= descriptionBottomEdgeY)
return;
//push the bottom of image to the bottom of description
imageBottomEdgeY = descriptionBottomEdgeY;
float newImageHeight = imageBottomEdgeY - imageRect.origin.y;
imageRect.size.height = newImageHeight;
self.cellImageView.frame = imageRect;
CGRect cellFrame = self.frame;
cellFrame.size.height = imageRect.size.height + imageRect.origin.y + 5;
CGRect contentFrame = self.contentView.frame;
contentFrame.size.height = cellFrame.size.height - 1;
self.contentView.frame = contentFrame;
self.frame = cellFrame;
}
It pretty much tells that if description is taller than image, we must resize the image as well as cell height to fit the description.
However when I invoke this code by doing this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.cellDescriptionLabel.text = #"Some long string";
[cell.cellDescriptionLabel sizeToFit];
[cell setNeedsLayout];
return cell;
}
It appears that while cell frame is changed due to layoutSubViews call, other cells do not respect it. That is, they appear on the same position had the previous cell would not have resized itself.
Two questions:
How to make it possible what I want?
Am I doing right by calling setNeedsLayout within cellForRowAtIndexPath?
P.S.: I know heightForRowAtIndexPath holds key to changing the cell height, but I feel that the data parsing (not shown here) that I do as part of cellForRowAtIndexPath would be an overkill just to calculate height. I need something that can directly tell the UITableViewCell to resize itself according to content needs.
-tableView:heightForRowAtIndexPath: is by design how variable sized cells are calculated. The actual frame of a cell is of no importance and is changed by the table view to fit its needs.
You are sort of thinking of this backwards. The delegate tells the table view how cells need to be drawn, then the table view forces cells to fit those characteristics. The only thing you need to provide to the cell is the data it needs to hold.
This is because a table view calculates all the heights of all the cells before it has any cells to draw. This is done to allow a table view to size it's scroll view correctly. It allows for properly sized scroll bars and smooth quick-pans through the table view. Cells are only requested when a table view thinks a cell needs to be displayed to the screen.
UPDATE: How Do I Get Cell Heights
I've had to do this a couple of times. I have my view controller keep a cell which is never used in the table view.
#property (nonatomic) MyTableViewCell *standInCell;
I then use this cell as a stand in when I need measurements. I determine the base height of the cell without the variable sized views.
#property (nonatomic) CGFloat standInCellBaseHeight;
Then in -tableView:heightForRowAtIndexPath:, I get the height for all my variable sized views with the actual data for that index path. I add the variable sized heights to my stand in cell base height. I return that new calculated height.
Note, this is all non-autolayout. I'm sure the approach would be similar, but not identical to this, but I have no experience.
-tableView:heightForRowAtIndexPath: is the preferred way to tell tableview the size of its cells. You may either precalculate and cache it in a dictionary and reuse, or alternatively in ios7, you can use -tableView:estimatedHeightForRowAtIndexPath: to estimate the sizes.
Take a look at this thread - https://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights, the answer points to a very good example project here - https://github.com/caoimghgin/TableViewCellWithAutoLayout.
Sorry, but as far as I know you have to implement tableView:heightForRowAtIndexPath:. Warning, in iOS 6 this gets called on every row in you UITableView right away, I think to draw the scrollbar. iOS7 introduces tableView:estimatedHeightForRowAtIndexPath: which if implemented allows you to just guess at the height before doing all the calculation. This can help out a lot on very large tables.
What I found works well is just have your tableView:heightForRowAtIndexPath: call cellForRowAtIndexPath: to get the cell for that row, and then query that cell for it's height cell.bounds.size.height and return that.
This works pretty well for small tables or in iOS7 with tableView:estimatedHeightForRowAtIndexPath implemented.
to correctly embed a UITableView into a UIScrollView I need to know the exact height of the UITableView. I added the count of rows * rowHeight and the count of section headers * sectionTitleHeight, but this is still roughly 50px off the point. I guess it is caused by the empty table header and footer, but I can't find any information about how high they are...
Did I miss something in the UITableView class ref?
Thanks for any tips,
nobi
You can get the UITableView's frame variable and check its size member variable to see the exact size of your UITableView element.
CGSize tableSize = [table frame].size;
CGFloat tableHeight = tableSize.height;
CGFloat tableWidth = tableSize.width;