I'm trying to create a custom cell with 4 UILabels and a UITextView. I've laid out the elements. The UITextView is not scrollable. The problem I face it placing the last UILabel under the UITextView. I've tried pinning both(either) to the superview and nothing seems to work. It is always under the UITextView no matter what as shown below:
What more should I do to move the UILabel below the UITextView?
This the only change I've made in my code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
QXTInboxCell *inboxCell = (QXTInboxCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
CGFloat width = self.tableView.frame.size.width - inboxCell.question.frame.origin.x - QUESTION_RIGHT_MARGIN;
CGSize size = [inboxCell.question sizeThatFits:CGSizeMake(width, 400)];
[inboxCell.question sizeThatFits:size];
return (inboxCell.question.frame.origin.y + size.height + 20);
}
Please help. I'm on XCode 5.0.2. Thanks.
Add the following constraints:
TextView to superview (top space)
TextView to label (bottom/top space)
label to superview (bottom space)
then (AFTER you set the text of the textView) call
[cell setNeedsLayout]
Of course this only works if you calculated the correct height for the cell in heightForRowAtIndexPath:.
Related
I am trying to set height of UILabel dynamically in the UITableView. During the launch height update is not reflected but as soon as I scroll down and scroll up back, update can be seen.
At Launch
After Scroll down and Scrolling back up again - This what I need. See the change in text in front of player icon. I need the complete text at launch itself.
Here is the code that I am trying to use:
- (void) updateMessageTextForRow:(long)row ofCell:(ESGameStreamCellView *)cell
{
NSString *item = _gameFeedItems[row];
NSString *title = item ?: NSLocalizedString(#"[No Title]", nil);
cell.message.preferredMaxLayoutWidth = cell.message.bounds.size.width;
// Update message label height
CGSize maximumLabelSize = CGSizeMake(296, FLT_MAX);
CGSize expectedLabelSize = [title sizeWithFont:cell.message.font
constrainedToSize:maximumLabelSize
lineBreakMode:cell.message.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = cell.message.frame;
newFrame.size.height = expectedLabelSize.height;
cell.message.frame = newFrame;
NSLog(#"Message = %#, Height: %f", title, cell.message.frame.size.height);
}
During Custom TableCellView Initialization
- (void)awakeFromNib {
// Initialization code
self.backgroundColor = [UIColor clearColor];
_message.lineBreakMode = NSLineBreakByWordWrapping;
_message.numberOfLines = 0;
}
Code for the row height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Currently fixed height. Will be calculating dynamic height after adjusting the views.
return 300;
}
Are you using autoLayout ? I highly suspect that your constraints aren't set up properly: the UIImageView top should by tied with the titleLabel's bottom.
Also, you should use the new property for dynamic row : rowHeight and estimatedRowHeight.
You are getting the correct height after the cell's reuse : set the preferredMaxLayoutWidth property in the viewDidLayoutSubviews inside your custom cell class.
firstly, you should understand of the working flow of tableview delegate in objective c. Your cell height & position will be fixed after init. that's why you have to define each row's height in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Currently fixed height. Will be calculating dynamic height after adjusting the views.
return 300;
}
From this point forward, your cell's height will be fixed, even if you re-config the frame.
The best practice is some article called "Dynamic height tableview cell" and you can easily find it here
http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift
I learn on above article (thanks for Joshua Greene) and re-write it to another library allow you to make a dynamic tableview easily. You can find it here
https://github.com/EugeneNguyen/XBMobile
it's not too perfect, but hope that if can help.
I have to show the list details in UITableview. But the listing is small for some cells and large/medium height for some other cells. (Like facebook feed) So Tableview have different height for each cell. How can i achieve this?
The list contains,
imageview,2 labels, textview (Label and textview lengths also increased based on its content. It will also affect the tableview cell height). So any help to achieve this would be greatful.
Thanks.
Create your cells with their own .xib. Add the labels, textview imageviews etc as you want. Make sure to pin all them with constraints horizontally and vertically to the leading/trailing/top and bottom of the cell.
Now the trick is to tell autolayout to layout the contentView of the cell, and return the minimum height to fit its content. Since heightForRow is executed before the cell is loaded in cellForRow, we need to use a fakeCell to calculate the height.
To do this lets say, in our controller, we have a tableView showing cells of class DetailsTableViewCell with a nib created for it DetailsTableViewCell.xib :
1) Create a #property in your controller for a fakeCell.
#property (nonatomic, strong) DetailsTableViewCell *fakeCell;
2) In viewDidLoad, initialize it from them nib:
self.fakeCell = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass:#"DetailsTableViewCell" owner:nil options:nil].firstObject;
3) In heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//grab fakeCell, style it with the data you want
[self.fakeCell styleWithDetails:self.details];
//and calculate the minimum height required to fit all content
return [self minimumCellHeightToSatisfyConstraints:self.fakeCell];
}
-(CGFloat)minimumCellHeightToSatisfyConstraints:(UITableViewCell *)cell {
//Makes sure cell's width is equal to tableView's width (can be wrong since they are in separated nibs)
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(cell.bounds));
//Force autolayout to recalculate all subview's frames
[cell layoutIfNeeded];
//Get the minimum height required for the cell to fit all its content
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;
}
Important: If you forget to pin all subviews in the .xib vertically from top to bottom to the cell, calling systemLayoutSizeFittingSize:UILayoutFittingCompressedSize will return 0 and won't work.
#define CELL_MARGIN 10.0f
-(CGFloat)heightForTableView:(NSString*)text widthOfTableview:(CGFloat)tableViewWidth{
CGSize constraint = CGSizeMake(tableViewWidth - (CELL_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_MARGIN * 2);
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self heightForTableView:cell.titleLabel.text widthOfTableview:CGRectGetWidth(tableView.frame)];// cell.textlable.text is your lable.text
}
This code might helps you :)
I'm trying to adjust a UITableViewCell's size to its content. This is basically for a chat view, where I list all previous messages and allow the user to scroll the conversation content.
So I have a UITableView with a few different cell prototypes. Incoming text messages, outgoing text messages, incoming image messages, outgoing image messages, and so on. Inside each cell's content view I have a standard UIView which I intent to use to draw the chat balloon. This view takes almost the cell's inner space (8px offset to the top, left, bottom, and right, all around). Inside that view I want the content. In the case of the text cells (incoming and outgoing) I want a UITextView which will display a text message. This is what I mean:
In yellow is the UIView and inside it the UITextView. Now I want to adjust everything to the text's size. I managed to accomplish the following:
sizeToFit accomplishes exactly what I need for the UITextView
I'm still not sure how to adjust the UIView's size to the UITextView's size.
To adjust the cell's height maybe I could use heightForRowAtIndexPath. I don't need (nor do I think I should) to adjust the cell's width. But a few regards on that: when is this method called? Will the cell already have been instantiated? Will it have already layed out the subviews? Otherwise, how can I tell the content's size?
Any input on this is appreciated!
Edit:
I managed to make a few progresses by following the tutorial posted by #vikingosegundo, but I'm stuck again. This is what I have:
So, basically: the text view has constraints for leading, trailing, distance to top, and distance to bottom. The containing view, on the other hand, has constraints for trailing and distance to top, so that if the size is small then it snaps to the right. I can't had leading constraints or otherwise it will always take the full width of the cell. I'm not sure about distance to bottom constraints.
When a enter a small message it looks great. It's well sized and it snaps to the right.
However, long messages don't span to several lines. Instead it still snaps to the right (OK), but the width grows to the left, indefinitely.
The cell is already adjusting its height to the content's height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
HyConversationTableViewCell * cell = (HyConversationTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
CGSize size;
[cell setNeedsLayout];
[cell layoutIfNeeded];
size = [cell.textMessageView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 32;
}
I'm guessing that what I need now is something like a text view's maximum width or something, but I realise that's not possible. How do I solve this?
Edit: If I had a leading constraint to the containing view it looks great when the text spans multiple lines, but not when it doesn't. Here's what it looks like:
And:
Edit: As suggested by Alex Zavatone, I changed tableView:heightForRowAtIndexPath: to the following:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
HyConversationTableViewCell * cell = (HyConversationTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
CGSize overflowSize = CGSizeMake(cell.textMessageView.frame.size.width, FLT_MAX);
CGSize sizeAdjusted = [cell.textMessageView sizeThatFits:overflowSize];
return sizeAdjusted.height + 32.0f;
}
It shows a little better as the height is already adjusted, but the behaviour is somewhat erratic. Here's what it looks like at the beginning:
So the height is correct, but the text view does not adjust its width. Also, if I scroll the cells out of screen and then back in (which forces them to redraw) they start behaving erratically in what seems a random criteria. Here's a sample:
Sometimes this happens to the last two cells...
Edit: That last part was fixed by setting Content Hugging Priority and Content Compression Resistance Priority to required and the Intrinsic Size to Placeholder. Now the height shows properly.
If you are using iOS 8 you can use UITableViewAutomaticDimension.
You can check out this example
self.tableView.rowHeight = UITableViewAutomaticDimension;
You can take a look also on this video : What's New in Table and Collection Views in the 2014 WWDC.
Here how, we are doing that
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
AFMediaWithHeadlineCell *cell = [[[NSBundle mainBundle] loadNibNamed:#"AFMediaWithHeadlineCell"
owner:nil
options:nil] firstObject];
[cell loadText:#"Some text"];
return [cell height];
}
actually loadText: loads data into UI, and sizeToFits it.
and height is basic method that calculates cell's height
- (void)loadText:(NSString *)aText
{
self.textView.text = aText;
[self.textView sizeToFit];
}
- (CGFloat)height
{
return self.textView.frame.origin.y + self.textView.frame.size.height + 10; // 10 is margin
}
You can resize the text by setting the height to a large number and then using sizeThatFits on it.
Not sizeToFit.
Like so:
UILabel *label = self.prototypeCell.descriptiveText;
label.numberOfLines = 0;
CGSize sillyLargeHeight = CGSizeMake(label.frame.size.width, 9999);
CGSize labelFrameAdjustedForHeight = [label sizeThatFits:sillyLargeHeight];
return labelFrameAdjustedForHeight.height + 24.0; // 24 is 12 above and 12 below padding.
You can use a label or a textView. If you choose to use a UILabel, you'll need to set the # of lines to 0 so that it will be multiple line.
You can do this within - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
You'll also need to do the same adjustments to set the height on the field (use an IBOutlet) in the willDisplayCell method.
I have a summary cell that has it's height calculated using systemLayoutSizeFittingSize. It mostly works as expected. The height is determined by three multi-line labels (title, author, genre) and an ratings image view, with the outer elements tacked to the contentView.
When the title label overflows into the next line, it sizes appropriately. However when the author label overflows, it doesn't seem to increase the size appropriately.
All the compression resistances on the labels and image view are maxed out at 1000. There is an lower priority constraint on the bottom of the thumbnail to the left, in case the content to the right is smaller than the thumbnail. (#750, bottom == 8 from superview bottom). The ratings image view has a constraint to the bottom of the superview as well (#1000, bottom >= 8 from superview bottom).
Interesting. So I ended up fixing this by always resetting the reference cell frame height before updating the content and calculating the height. Not entirely sure why this step is needed. I have a couple guesses around the autoresize constraints on the contentView taking precedence:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case TableSectionSummary: {
TCAnswerDetailAppSummaryCell *cell = self.summaryCell;
CGRect oldFrame = self.summaryCell.frame;
cell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, mTCTableViewFrameWidth, 400);
[cell configureWithThirdPartyObject:self.app];
[cell updateConstraintsIfNeeded];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
return height;
}
case TableSectionDetails:
return [TCAnswerDetailBasicCell heightForCellWithTableWidth:mTCTableViewFrameWidth withLabelString:self.app.appDescription withDisclosureArrow:NO];
}
return 0;
}
I am trying to create multi-line dynamic UILabels in UITableViewCells. I have a custom UITableViewCell that has a 'comment' label. The cell and the label are created in storyboard.
I can compute the heights of the UITableViewCells properly based on the multi-line data to be stored in the UILabel (using heightForRowAtIndexPath). However, my problem lies in the actual UILabel content. The UILabel content will display only 1 line of data on table load. However, once a cell containing multiline UILabel data moves offscreen and comes back on screen, the multi-line data appears properly in the UILabel with multiple lines. Is there any way to fix this so that the multi-line data appears properly on table load?
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cCell = (CustomCell *)cell;
MyObject = [myArray objectAtIndex:indexPath.row];
cCell.commentLabel.frame = CGRectMake(65.0f, 28.0f, 243.0f, 200.0f);
cCell.commentLabel.text = MyObject.multi_line_text_data;
cCell.commentLabel.adjustsFontSizeToFitWidth = NO;
cCell.commentLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
cCell.commentLabel.font = [UIFont systemFontOfSize:13.0];
cCell.commentLabel.lineBreakMode = NSLineBreakByWordWrapping;
cCell.commentLabel.numberOfLines = 0;
[cCell.commentLabel sizeToFit];
}
Thanks!
Since you're doing this in the storyboard, you can set the necessary label properties there (lineBreakMode and number of lines). Just give the label a specific width constraint and constraints to the top, bottom, and left sides of the cell. Then, in code use sizeWithFont:constrainedToSize:lineBreakMode: in heightForRowAtIndexPath: to calculate the appropriate height for the cell based on the content of the label -- the label, because of its constraints, will expand along with the cell to the proper size. Something like this:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize rowSize = [self.theData[indexPath.row] sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(260, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
return rowSize.height + 30;
}
Here, 260 was the width I gave my label in IB, and the 30 is a fudge factor (determined empirically) to account for padding above and below the label.
I met the same problems. Unchecking Autolayout can fix it.