Setting dynamic UITableViewCell height - ios

I've got a UITableView with two dynamic rows. Each of the rows is a subclass of UITableViewCell and is loaded from nib. As my rows contain dynamic content, I use layoutSubviews to reposition all subviews:
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat initialHeight = titleLabel.bounds.size.height;
CGSize constraintSize = CGSizeMake(titleLabel.bounds.size.width, MAXFLOAT);
CGSize size = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:constraintSize];
CGFloat delta = size.height - initialHeight;
CGRect titleFrame = titleLabel.frame;
titleFrame.size.height += delta;
titleLabel.frame = titleFrame;
locationLabel.frame = CGRectOffset(locationLabel.frame, 0, delta);
dayLabel.frame = CGRectOffset(dayLabel.frame, 0, delta);
timeLabel.frame = CGRectOffset(timeLabel.frame, 0, delta);
}
The problem is that I can't find a way to determine the height in table view delegate's tableView:heightForRowAtIndexPath: method.
The trick is that I load cell from nib, so just after it's loaded titleLabel.bounds.size.width is 300 px (as in nib), not taking into account type of the device (iPhone/iPad) and current orientation, so it seems impossible to calculate the height without conditional checks for orientation and device type. Any ideas?

Your layoutSubviews doesn't resize the cell, so the following should work:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return cell.frame.size.height;
}
If the cell needs to be resized due to the content changing then you can set self.frame = newFrame inside layoutSubviews.
You can also manual cause layoutSubviews to be called by calling setNeedsLayout.

Related

How to get correct width of a UIView inside UITableViewCell?

I added a UICollectionView inside UITableViewCell. I am trying to set the collectionView.collectionViewLayout.itemSize.width equal to a UIView pageContainerView.bound.size.width inside table view cell. I am trying to set the item size in awakeFromNib. The pageContainerView is added using autolayout, it has both 8 leading and trailing to the super view.
- (void)awakeFromNib {
[super awakeFromNib];
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
CGFloat width = self.pageContainerView.bounds.size.width;
CGSize size = CGSizeMake(width, 321);
layout.itemSize = size;
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
}
But the item size width is shorter than its expected.
You can get the correct size in layoutSubviews
- (void) layoutSubviews {
[super layoutSubviews];
CGFloat width = self.pageContainerView.bounds.size.width;
CGSize size = CGSizeMake(width, 321);
self.layout.itemSize = size;
[self.layout invalidateLayout];
}
I believe the issue here is that you are setting the size of the cell before it is actually displayed on the screen, therefore the whole cell isn't actually the size of the screen yet. I would try setting the cell size when you configure the cell (in cellForItemAt).

Autolayout with dynamic height

I am trying to create a subclass of UIView that will show a list of fixed sized UIImages similar to how a UILabel displays letters.If all the images won't fit on one line, the images are arranged on multiple lines.
How can I achieve this using autolayouts so that if I put this view in a UIStackView the images will be listed correctly?
Here is a sample if I did it using fixed position :
- (void) layoutSubviews{
[super layoutSubviews];
CGRect bounds = self.bounds;
CGRect imageRect = CGRectMake(0, 0, 30, 30);
for (UIImageView* imageView in self.imageViews){
imageView.frame = imageRect;
imageRect.origin.x = CGRectGetMaxX(imageRect);
if (CGRectGetMaxX(imageRect) > CGRectGetMaxX(bounds)){
imageRect.origin.x = 0.0;
imageRect.origin.y = CGRectGetMaxY(imageRect);
}
}
}
Update:
Here is a sample project to show the issue.
https://github.com/datinc/DATDemoImageListView
Here the link for ImageListView
https://github.com/datinc/DATDemoImageListView/blob/master/DATDemoImageListView/DATDemoImageListView.m
You should overwrite intrinsicContentSize returning the preferred content size of your view.
The problem is that in intrinsicContentSize the view has not it's final bounds. You can use an internal height constraint, and overwrite updateConstraints:
- (void)updateConstraints {
CGFloat theWidth = CGRectGetWidth(self.bounds);
NSUInteger theCount = [self.subviews count];
CGFloat theRows = ceilf(theCount / floorf(theWidth / 30.0));
self.heightConstraint.constant = 30.0 * theRows;
[super updateConstraints];
}
In viewDidAppear: and on layout changes (e. g. rotation) you have to call setNeedsUpdateConstraints to get a proper initial layout of the image views.

tableHeaderView with Autolayout not working correctly

Hey everybody i have a TableHeaderView and everything gets managed by Autolayout:
The UIImageView at the top should be always 2:1, so i set the Aspect ration and the Rest of the needed Constraints.
The 4 UIButtons should be always horizontal and should have the same Height and Width. So i worked with Equal Width and Equal Height and also with a Aspect Ratio of 1:1
And i have two UILabels with numberOfLines set to 0. I also made a Subclass of my UILabel, because of the preferredMaxLayoutWidth, like this:
- (void) layoutSubviews
{
[super layoutSubviews];
if ( self.numberOfLines == 0 )
{
if ( self.preferredMaxLayoutWidth != self.frame.size.width )
{
self.preferredMaxLayoutWidth = self.frame.size.width;
[self setNeedsUpdateConstraints];
}
}
}
This is my Code:
- (void)initializeHeaderViewLabels
{
//After the Response from my server arrived i set the tableHeaderView with the Textdata
self.tableView.tableHeaderView = nil;
if((self.contentDict[#"title"]) != [NSNull null])
{
self.headerTitleLabel.text = (self.contentDict[#"title"]);
}
if((self.contentDict[#"shortDescription"]) != [NSNull null])
{
self.headerDescriptionLabel.text = (self.contentDict[#"shortDescription"]);
}
[self.headerView setNeedsLayout];
[self.headerView layoutIfNeeded];
CGFloat height = [self.headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = height;
self.headerView.frame = headerFrame;
[self.tableView setTableHeaderView:self.headerView];
}
I get my Image and the Text for the UILabels from my Server, so i have to wait until the Response arrives, then i call initializeHeaderViewLabels.
**My Problem is that the tableHeaderView is way too large during Runtime and so my UILabels get stretched and there is a lot of whiteSpace. Maybe i miss something?
In order to make it work you'll need some magic or migrate away from table header view. I've already answered on a similar question here. The trick is to magically reset tableHeaderView after evaluating it's height via autolayout. I've created sample project for that: TableHeaderView+Autolayout.
Try below solution
- (void)sizeHeaderToFit
{
UIView *header = self.tableView.tableHeaderView;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect frame = header.frame;
frame.size.height = height;
header.frame = frame;
self.tableView.tableHeaderView = header;
}
Source:
table header view height is wrong when using auto layout, IB, and font sizes
Also refer below question...!
How do I set the height of tableHeaderView (UITableView) with autolayout?
My problem was that I was setting constraints on the my header view that were mapping some frame values to superview frame values. When I removed the constraints and just set the frame directly, everything worked.
Header view height is set in the table view delegate. By default it is the same size as in interface builder which is too large when run in app since the width for interface builder is 600.
So what you need is to implement this method in UITableViewDelegate
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 200.0 // Set the desired height based on your device or what ever you are using.
}
And Objective-C version:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 200.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 200;
}

Dynamically size UITableViewCell's height

I have a UITableViewCell with multiple items inside. (Not just a textView so I cant follow this option.) I'm trying to dynamically size it's height based on the content it has inside.
The heights I will be changing, are a UITextView and a UIView. The textView will constantly be changing (at another method, if you'd like, I can post it). And the UIView will change if the user clicks a button:
Here is my code:
- (void)viewDidLoad
{
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (IBAction)thisButton:(id)sender
{
CGRect frame = self.myView.frame;
frame.size.height = 50;
frame.size.width = self.myView.frame.size.width;
self.myView.frame = frame;
// update 'myView's constraint
self.viewHeight.constant = self.myView.frame.size.height;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[myTableView reloadData];
}
Problem:
What happens is, when I press the button, the UIView's height gets
updated, but then everything else in the cell gets moved up, and the cell stays the same size.
When the UITextView's height changes, it doesn't pull everything else
down, and the cells height stays the same. Though the textView's
height does change and it just goes over everything else.
Constraints:
On the UITextView I have 3 constraints - 2 on each side, and 1 on top. The UIView has 3 constraints - 2 on each side, and 1 on the bottom.
I then have a constraint connecting the UIView to the textView.
You can dynamically manage UITableViewCell size by calculating a max height of your internal views:
NSArray *array = [[NSArray alloc] initWithArray:#[view1, view2, view3]];
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
__block CGFloat maxHeight;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIView *view = obj;
if (view.bounds.size.height > maxHeight) {
maxHeight = view.bounds.size.height;
}
}];
return maxHeight;
}
Then in the end of your method that changes view's dimensions you must call [self.tableView reloadData]

UITableView frame base on number of cell

As My title said that i want to set frame of UITableView base on number of cell. Cell's height of UITableView is dynamic it is not fix, for do this i apply my following logic
NOTE: I added UITableView on UIScrollView so it is easy to scroll/see whole table's content. And i know UITableView has own scrollView but in my project i need set height of UITableView base on number of cell (dynamic height of cell).
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self adjustHeightOfTableview]; // custom method for set height of tableView
}
- (void)adjustHeightOfTableview
{
CGRect frame = self.tblView.frame;
frame.size.height = tblHeight; // tblHeight is CGFloat, declare in .h file
self.tblView.frame = frame;
self.scrollView.contentSize = CGSizeMake(300, self.tblView.frame.origin.y + self.tblView.frame.size.height);
}
And get from
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", indexPath.row);
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:indexPath.row] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
return height + (10.0f * 2) - 1.5;
}
But problem is that heightForRowAtIndexPath call 2 times so i am not able to get proper tblHeight so my tableView's height not set properly.
Why my heightForRowAtIndexPath call 2 times or How can i solve my problem ?? or any other solution for set tableView's frame base on dynamic number of cell ??
Just for understanding
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
why to not make empty footerView.
UIView *footerView=[[UIView alloc]initWithFrame:CGRectZero];
self.taskListTable.tableFooterView=footerView;
and then change the table frame where you thinks it will change:
like in viewWillApear or any of your action
self.taskListTable.frame= CGRectMake(0,0, 320, self.view.bounds.size.height);
Each row will call heightForRowAtIndexPath everytime it will appear in the visible rows, so the solution is quite simple.
Create an NSMutableArray, with number of rows as your table view, then everytime you calculate the height of a row, just change that array's index for that indexPath's row with the height of that row.
And whenever you want the height of the table, just sum all your array's rows, that should do it.
If you have any more clarifications, just ask (Y)
heightForRowAtIndexPath will call for each row which is going to visible.
You can calculate table height in viewDidAppear or ViewWillApearMethod.
for (int i =0 ; i < self.listOfQuestions.count; i++) {
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:i] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
}
Check my updated answer

Resources