I need to build an iOS screen whose objective is to show information about a hotel, with a "big" scroll of things.
My main problem is that I have to join a UITextView with a dynamic height and two lists below, each one with it own header. The lists must have a dynamic height two.
I don´t know how can I do it. I am thinking in to use UITableView and make each component a cell, including the lists, but I don´t know how to start. Should I do it only programatically or is it possible using storyboards?
The mockup is below:
Mockup
Fixing UILabel cell size (already working)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text = motel.descricao;
CGSize constraintSize = CGSizeMake(320.0f, MAXFLOAT);
//You will need to define kDefaultCellFont
CGSize labelSize = [text sizeWithFont:[UIFont systemFontOfSize:13]
constrainedToSize:constraintSize
lineBreakMode:UILineBreakModeWordWrap];
return labelSize.height + 30;
}
I used inner tableview in a uitableviewcell. The problem is to manage the delegates.
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've done a lot of reading already on this, but it's seems like there's no simple solution.
I'm trying to make an app that loads an NSArray of NSString comments to display in a UITableView. The comments all vary in size.
How can I get the cell to adjust its size to show the entire content of each comments?
I'm trying to find a simple solution without resorting to using magic CFloat numbers.
Is there a method in apple's API that allows me to calculate the needed height of the cell.detailTextLabel given an NSString comment and fixed width?
I think if I can calculate this height, all that's need is to set the height of the cell and the height of the row.
Not really sure what order to do this in since I've read the cell hasn't been created yet when heightForRow:AtIndexPath: gets called.
Calculate the height of the text in tableView:heightForRowAtIndexPath:
You need to use boundingRectWithSize:options:context: on iOS 7+ and sizeWithFont:forWidth:lineBreakMode: on iOS 6 and below. See the apple documentation for more information.
UIFont *font = [UIFont oka_commentLabelFont];
NSString *text = [self commentForIndexPath:indexPath];
CGFloat cellWidth = 300.f;
CGSize boundingSize = CGSizeMake(widthForCell, CGFLOAT_MAX);
CGSize size;
if ([text respondsToSelector:#selector(boundingRectWithSize:options:attributes:context:)]) {
size = [text boundingRectWithSize:boundingSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName : font }
context:nil].size;
} else {
size = [text sizeWithFont:font
constrainedToSize:boundingSize
lineBreakMode:NSLineBreakModeWordWrap];
}
return size.height;
You need to set the font to the same font you want to use on the cell. If you put this in a performUpdates block on the tableView then you will get a nice expanding animation.
Don't set the height of the cell, set the height of the row and the table view will do the rest.
To calculate the height of the text, ask a label with textRectForBounds:limitedToNumberOfLines:, or ask the string with one of the many methods like sizeWithFont:constrainedToSize:lineBreakMode:.
You may need to deal with padding around the label when compared to the size of the cell.
yes we can calculate the height of UILabel depending on the Font and line break mode.
UIFont * font = [UIFont systemFontOfSize:12.0];
CGSize newSize = [text sizeWithAttributes:#{NSFontAttributeName:font}];// You can add other attributes in dictionary to like line break mode.
Here you got new size for the text which will be in UILabel, now in hegihtForCellAtIndexPath method you can change the size of UITableViewCell. newSize.height is height you need for label, now you can add some value to it for offsets and return it.
Use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
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.
In my case, how would I resize a UITableViewCell to accomodate the height of a UITextView that it contains?
I'm currently trying to achieve this using the following code:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// init the font that we use
UIFont *font = [UIFont fontWithName:#"SourceSansPro-Regular" size:16];
if (indexPath.section == 0) {
// get the text size of the thread
CGSize textSize = [_thread.text sizeWithFont:font constrainedToSize:CGSizeMake(280, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
return textSize.height + 20;
}
// default
return 60;
}
I feel like if I could just get access to the UITableViewCell in this method, then I could use the contentSize property of the UITextView in it, and return that. But I don't think that's possible at this point in the execution flow.
I can't just tweak that buffer, because it doesn't scale with the text size being returned by the get size function. The shorter the text, the more visible a larger buffer size is.
This is adjusting the height of the UITableViewCell, but it's not showing all of the UITextView inside of it. I've specified the width of the UITextView in the sizeWithFont:constrainedToSize:lineBreakMode: method, which is 280. I've specified that I wish to have essentially no maximum height, so that it can word-wrap as much as it needs to, using the CGFLOAT_MAX constant. I append a buffer of 20 units to the resulting height of the method call, because there is a 20 unit buffer above the UITextView, in my Storyboard, and I'd like to also have a 20 unit buffer below it.
The end result of all of this still has text being clipped off, though. Here is a picture of what I am talking about:
Any ideas?
I'm going to go ahead and answer my question. The answer to this comes in two steps.
First, adjust the size of the UITextView during your logic inside of the tableView:cellForRowAtIndexPath: method. Make sure to do this after making any changes to the text in the UITextView. I re-size my UITextView using the following method, inside of a subclass of UITableViewCell where self.text is the text view:
- (void)resizeTextView {
// get the current frame size
CGRect frame = self.text.frame;
// set it to the height of the text view
frame.size.height = self.text.contentSize.height;
// set the new size of the text view
self.text.frame = frame;
}
Next, I specify the height of the UITableViewCell inside of the tableView:heightForRowAtIndexpath: method. This is where most of the mystery exists, because to get the height for this row you have to calculate the height of any text that is going to be displayed in the row, along with anything else you need to calculate the height of. In my scenario, is was a UITextView. Without access to the UITableViewCell instance in this method, you're forced to use something like sizeWithFont:constrainedToSize:lineBreakMode:. But, I noticed this was not returning consistent heights for me. Or at least, not heights that seemed to make enough room for my text from the UITextView. That was the problem, though. The text view has additional left and right padding as well as different line heights, or something, than the sizeWithFont method uses.
So, after fudging the results of the sizeWithFont method for awhile, I ended up with the following code:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// init the font that we use
UIFont *font = [UIFont fontWithName:#"SourceSansPro-Regular" size:16];
if (indexPath.section == 0) {
// get the text size of the thread
CGSize textSize = [_thread.text sizeWithFont:font constrainedToSize:CGSizeMake(280 - 16, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
// text height + padding
return textSize.height + 40;
} else if (indexPath.section == 1) {
// get the comment for this row
Comment *comment = [_comments objectAtIndex:indexPath.row];
// get the heights of the text areas
CGSize textSize = [comment.text sizeWithFont:font constrainedToSize:CGSizeMake(280 - 16, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
return textSize.height + 40;
}
return 60;
}
In the size constraint parameter, I subtract 16 units because the UITextView has a padding of roughly 8 units on both the left and right sides. This will give us a closer result to what we expected for the height of the text.
I also add some padding to the final height, of 40 units, because the row itself needs some padding around the UITextView.
There you go! Hope that helps.
How can I get the second cell to expand to fit the text rather than scaling the text? Is there a built in way of doing this in iOS or will I have to come up with some home-cooked solution? If you look in the iOS Contacts application, there's a box like this for Address. I can't find how to implement this though.
For anyone looking to achieve this in future, here's the code for my solution:
HEADER file:
#define FONT_SIZE 22.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 5.0f
IMPLEMENTATION file:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == 1) {
NSString *text = [atmAnnotation address];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
NSLog(#"Size for address is Height:%f Width:%f",size.height,size.width);
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_CONTENT_MARGIN * 2);
}
return 44.0f;
}
Here's a screenshot of the result:
Unfortunately, you are going to have to implement this feature yourself. There are a variety of methods and callbacks you need to make use of to calculate the height of the rows and labels. If you need some help getting started, I can amend this answer to include some sample code. Otherwise, I'm sure there are some related questions here on SO or Google that can get you started. In summary, however:
Determine the height of the row in the UITableViewDelegate method -tableView:heightForRowAtIndexPath:. You'll probably need to use the NSString UIKit Addition methods to calculate how tall your string will be with a given font size, etc. Return the proper height from this delegate method.
Make sure your label (either in IB or configured programmatically) is set to use multiple lines. If you set numberOfLines to 0, the label will use as many lines as necessary.
In your UITableViewDataSource implementation of -tableView:cellForRowAtIndexPath:, you'll need to use the same logic as before to determine the frame of your labels. Set the text you want, then change the frame so that the label is tall enough to just barely fit all the text.
Where you have the address as the detailTextLabel, use a UILabel thats supports as many lines as you need, for an address, 3 should be enough. Together with Paul Bently's answer, you should be good to go.
Implement:
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
in your table delegate. For the different row and section in the passed indexPath, return a different height to fit your label.
Ensure 'adjust to fit' for the label in IB is not ticked (property adjustsFontSizeToFitWidth if done using code).
I just ran into this same problem. I found an alternative solution that doesn't require quite as much hardcoding, and allows you to make modifications to the label in Interface Builder that will be reflected in the height calculation.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == 1) {
NSString *text = [atmAnnotation address];
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
cell.textLabel.text = text;
[cell.textLabel sizeToFit];
CGFloat height = MAX( 44, cell.textLabel.bounds.size.height );
return height + CELL_CONTENT_MARGIN * 2;
}
return 44.0f;
}
The only downside is that this will cause some additional temporary UITableViewCell allocations, but the table view will immediately reclaim them, so I don't think it should be a big problem.