I have a UILabel inside a UITableViewCell and I was trying to adjust the height, however when the height is greater than the cell height it overflows to the next cell below it. How can I avoid this? I am adding this into my contentView:
[self.contentView addSubview:self.commentsText_];
if you want to hide the overflows.
self.contentView.clipsToBounds = YES;
or you may want to layout by overwriting at
- (void)setNeedsLayout
{
[super setNeedsLayout];
self.commentsText_.frame = .... // layout your label
}
Using following code you can calculate height of label and also change the height of cell
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UILabel *yourlabel;// use your memober class UILabel. I am declare here temporary.
CGSize s = [yourlabel.text sizeWithFont:[UIFont systemFontOfSize:15] // enter your text font size and cell-width
constrainedToSize:CGSizeMake(yourcellwidth, MAXFLOAT) // - 40 For cell padding
lineBreakMode:UILineBreakModeWordWrap];
return s.height; //this will give you height of UILabel view you can change using addition according your requirements
}
Hope, this will help you..
Related
I'm coding a custom UITableViewCell object and I've implemented layoutSubviews to resize the cell when its contents is updated.
To implement heightForRowAtIndexPath, I'm calculating the height of the cell within the custom UITableViewCell object. I'm using NSString sizeWithFont to calculate the size of the cell based on the text within a UILabel and the width of a cell in the UITableView.
But how do I get the width of a cell in the UITableView?
Right now, I'm passing in the table view to the object and using the frame. But the width is of the whole table not the individual cells. Is there something I'm missing here?
Thanks in advance.
EDIT3
I was thinking, if it's really not possible, just test portrait or landscape orientation then set the width manually (ipad just has 2 different orientations)..
EDIT2
There have been some questions about 'why are you doing it this way xxxx'. I understand maybe there's a better way to achieve what I'm doing, creating a custom cell that can calculate its own height with varying text length. If there's a better way of doing it I'm all ears :)
EDIT
http://img845.imageshack.us/img845/7792/screenshot20121129at193.png
+ (CGFloat)getHeightForCellWithText:(NSString *)text isExpanded:(BOOL)expanded tableView:(UITableViewController *)tv {
ProposalViewCell *cell = [[ProposalViewCell alloc] initWithStyle:UITableViewCellSelectionStyleNone reuseIdentifier:#"NormalCell" tableView:tv];
[cell setLabelText:text];
[cell setExpanded:expanded];
[cell layoutSubviews];
return cell.primaryLabel.frame.size.height + cell.readmoreButton.frame.size.height + cell.sendmessageButton.frame.size.height +30;
}
- (void)layoutSubviews {
[super layoutSubviews];
_primaryLabel.numberOfLines = 0; // multiple lines
// size of expanded text label
// sizeWithFont: if text doesn't fit, it is truncated
CGSize expandedSize = [_primaryLabel.text sizeWithFont:myFont constrainedToSize:CGSizeMake(tableView.view.frame.size.width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
// size as expanded by default
_primaryLabel.frame = CGRectMake(10, 10, expandedSize.width, expandedSize.height);
if (expanded==NO) {
// size of summary text label
_primaryLabel.numberOfLines = 10;
CGSize summarySize = [_primaryLabel sizeThatFits:_primaryLabel.frame.size];
_primaryLabel.frame = CGRectMake(10, 10, summarySize.width, summarySize.height);
}
_readmoreButton.frame = CGRectMake(10, _primaryLabel.frame.size.height+10, 225, 25);
_sendmessageButton.frame = CGRectMake(10, _primaryLabel.frame.size.height+10+_readmoreButton.frame.size.height+10, 225, 25);
}
To get the width of the cell just do this...
cell.contentview.frame.size.width
I have to include a speech bubble as shown in the attached image as background in a table view cell.
But the bubbles keep varying in height based on the text length. What is the best way to implement this?
You need to calculate the cell height according to the text length, I usually make a class method in my custom UITableViewCell that do this.
+ (CGFloat)cellHeightForText:(NSString *)text
{
CGFloat cellHeight = 0.0;
// calculate cellHeight height according to text length
// here you set the maximum width and height you want the text to be
CGSize maxSize = CGSizeMake(kTextMaxWidth, kTextMaxHeight);
CGSize size = [text sizeWithFont:TEXT_FONT
constrainedToSize:maxSize
lineBreakMode:UILineBreakModeWordWrap];
// set some minimum height for the cell (if the text is too short..)
cellHeight = MAX(size.height, kMinHeight);
// here I usually increase the cellHeight according to the cell's other subviews
// because if you have other subviews under/above the bubble you need to count them
// and add height to the cell...
cellHeight += kSomeSpaceToAdd;
return cellHeight;
}
Then u call this method in heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
Message *currMessage = [self.myMessages objectAtIndex:indexPath.row];
CGFloat height = [MyCustomCell cellHeightForText:currMessage.text];
return height;
}
Of course you should also set the bubble image frame according to the text length, I something do this in the custom cell layoutSubviews method, at this point the cell height already set so you can use it (self.bounds.size.height) to set your bubble image accordingly..
I want to dynamicaly set the cell's rowheight to the UILabel's height (which is cell's subview).
The difficulty is UILabel needs to get its height by checking its string content
Two problems:
1- How can I set correctly the numberofLines in UILabel depending on the string lenght?
2- How can I reflect this UIlabel height to row.height because delegate method heightForRowAtIndexPath is called before the cellForRow method?
here is some code, whcih I am sure I do somethinsg wrong..
Nsstring st= "very long text...";
CGSize theSize = [st sizeWithFont:font constrainedToSize:CGSizeMake(250.0f, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
CGRect cg = CGRectMake(0.0f, 0.0f, 250.0f, theSize.height);
UILabel *label=[[UILabel alloc] initWithFrame:cg];
label.textAlignment=UITextAlignmentLeft;
label.text=st;
[label setNumberOfLines:10];// this is trouble!! how to set this!?
[cell addSubview:label];
- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath {
CGSize labelSize = CGSizeMake(200.0, 20.0);
if ([string length] > 0)
labelSize = [string sizeWithFont: [UIFont boldSystemFontOfSize: 17.0] constrainedToSize: CGSizeMake(labelSize.width, 1000) lineBreakMode: UILineBreakModeWordWrap];
return 24.0 + labelSize.height;
}
You need to implement heightForRowAtIndexPath. In my sample code, I returned 24 + the height of the label
(Mashup of my comments)
1- How can I set correctly the numberofLines in UILabel depending on the string lenght?
UILabel's numberOfLines is the maximum number of lines. "To remove any maximum limit, and use as many lines as needed, set the value of this property to 0."
2- How can I reflect this UIlabel height to row.height because delegate method heightForRowAtIndexPath is called before the cellForRow method?
You need to implement heightForRowAtIndexPath: and return the appropriate height for the row. You can move the computation of theSize to a helper method and use it from cellForRowAtIndexPath: too.
You can have a separate textForRow: function that you can both use to set the text of the label, and also to calculate the size of the label (and the cell) in the delegate method (much like you have already done)
I am trying to create a table with cells that contain UITextViews inside them for variable amounts of text. I need to specify the height of each cell to match the content size of the textviews. I am using...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITextView *historyNode = [[[UITextView alloc]init]autorelease];
historyNode.text = (#"%#",[globalArrayWithStrings objectAtIndex:[indexPath row]]);
NSLog(#"%2.0f",historyNode.frame.size.height);
return historyNode.contentSize.height;
}
For some reason, it always prints 0. If i print the height of a textview created in interface builder, it prints the correct value. Any ideas how to get around this or why I can't get the size of a textview until it is added to the view.
You can use the sizeWithFont: method to find the dynamic height. Your heightForRowAtIndexPath: should look like this.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = [globalArrayWithStrings objectAtIndex:[indexPath row]];
CGSize maxSize = CGSizeMake(textViewWidth, 999); // 999 can be any maxmimum height you want
CGSize newSize = [text sizeWithFont:aFont constrainedToSize:maxSize lineBreakMode:textViewLineBreakMode];
return newSize.height;
}
First set the frame of the uitextView and then you will get the height of the text view.
I would like to have a tablew view with a behaviour similar to the iPhone Contacts app by Apple: a uitableviewcell with a uitextview inside, so that when I write in the uitextview, the uitextview increases its height, and so accordingly the uitableviewcell dynamically adjusts its height. I searched over the whole web, finding only partial solutions and lack of sample code!
please help me I am desperate
Tony
Looking at this,you need to be somewhat tricky. You need to calculate the height of the textView dynamically and based on the Height of the TextView,you need to return the Height for the cell..
It's very easy & somewhat Tricky..
This is the code by which you can calculate the size of string....
First get the size of String
NSString *label = #"Sample String to get the Size for the textView Will definitely work ";
CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:CGSizeMake(320, 9999)
lineBreakMode:UILineBreakModeWordWrap];
over here ....
NSLog(#"%f",stringSize.height);
Secondly dynamically create the textView in the cell..giving the stringSize.height
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//}
NSDictionary *d=(NSDictionary *)[self.menuArray objectAtIndex:indexPath.section];
NSString *string = [d valueForKey:#"Description"];
CGSize stringSize = [string sizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:UILineBreakModeWordWrap];
UITextView *textV=[[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height+10)];
textV.font = [UIFont systemFontOfSize:15.0];
textV.text=string;
textV.textColor=[UIColor blackColor];
textV.editable=NO;
[cell.contentView addSubview:textV];
[textV release];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *d=(NSDictionary *)[self.menuArray objectAtIndex:indexPath.section];
NSString *label = [d valueForKey:#"Description"];
CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:CGSizeMake(320, 9999)
lineBreakMode:UILineBreakModeWordWrap];
return stringSize.height+25;
}
After giving so much pain to my fingers,......I think this is enough code...& will surely help to solve your problem..
Good Luck
Create a custom UITableViewCell and add your UITextView to the cell's contentView.
In LayoutSubviews, set textView.frame to the cell's contentView.bounds (or do some other custom layout).
When the textView contents change (discovered via UITextViewDelegate), do two things:
1) call [tableView beginUpdates]; [tableView endUpdates]; This will force the table view to recalculate the height for all cells (will call tableView:heightForRowAtIndexPath:). If your cell is the delegate for the textView then you'll have to figure out how to get a pointer to the tableView, but there are a few ways to achieve that. Or you could make the delegate your view controller...
2) when tableView:heightForRowAtIndexPath: is called for this cell, return the textView.contentSize.height. You can get your cell from here by calling [tableView cellForRowAtIndexPath]; Or if you just have one of these cells then cache a pointer to it in your viewController.
The only issue I found with the accepted answer was that we are allocating the UITextView each and every time. I found that this raised issues with typing into the view and having the text updating immediately and also keeping the view as first responder. When I tried to reload the cell with the new height it would then try to add a new textView.
Because of this I found a slightly different method to achieve the same goal. Hopefully this different take might help people who are struggling to implement the above code.
1) In the header file define a variable for the height of the text and the textView:
UITextView * _textView;
NSInteger _textHeight;
Setting a variable means that we can load the view to be a certain height if we are loading text into the textView and also reduces the complexity.
2) Load the text view and add it to our cell
_textView = [[UITextView alloc] init];
_textView.delegate = self;
_textView.returnKeyType = UIReturnKeyDone;
_textView.font = [UIFont fontWithName:#"Helvetica" size:16];
if (![_user metaStringForKey:bBioKey].length) {
_textView.text = #"Placeholder text";
_textView.textColor = [UIColor lightGrayColor];
}
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath == 0) { // Add logic to choose the correct cell
if (_textView.superview != cell) {
[cell addSubview:_textView];
_textView.keepTopInset.equal = KeepRequired(7);
_textView.keepBottomInset.equal = KeepRequired(7);
_textView.keepRightInset.equal = KeepRequired(10);
_textView.keepLeftInset.equal = KeepRequired(70);
}
}
}
}
Using keeplayout has enabled us to keep our textfield to always stay the same height as the cell. We are also only ever adding our UITextView once.
3) Add the code to calculate the height of the text
- (NSInteger)getHeightOfBio: (NSString *)text {
UILabel * gettingSizeLabel = [[UILabel alloc] init];
gettingSizeLabel.font = [UIFont fontWithName:#"Helvetica" size:16];
gettingSizeLabel.text = text;
gettingSizeLabel.numberOfLines = 0;
CGSize maximumLabelSize = CGSizeMake(240, 9999); // this width will be as per your requirement
CGSize expectedSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];
return expectedSize.height;
}
I have tried many and found this to work the best
4) Add some logic in the cell height to make use of this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) { // Set the height for our changing textView
return 30 + [self getHeightOfBio:_textView.text];
}
return 44;
}
Obvious we need a bit more height than our text height so I added an extra cushion amount which can be experimented with.
5) Refresh the view each time a character is typed to check if we need to increase the size:
- (void)textViewDidChange:(UITextView *)textView {
if ([self getHeightOfText:textView.text] != _textHeight) {
_textHeight = [self getHeightOfText:textView.text];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
}
In this section we get the height of the text each time the user types.
Then though we use our stored value and compare the current value to the stored value. Obviously if they are the same then there is no point in refreshing the view. If they are different we update the value and then refresh our table.
This bit I found a good answer on stackOverflow showing how we can refresh only the heights of the table instead of the cell itself. Why refresh the cell when we don't need to? This means that once this is called the cell height is updated and it increases nicely.
Anyway I found this worked really nicely and was simple enough that it can be put together or have different parts taken and put into other peoples pieces of code.
Props to the accepted answer which was pillaged for various pieces along the way but I also hope that this answer helps some people who are having the same difficulties that I had.
Finally I got it working. The main problem is how to get cell's contentView correct width. Hardcoded width does not work for all cases since it may vary accordingly plain/grouped table style, added accessories, or landscape/portrait layout. The only way to get 100% correct width is to ask it from cell object. So I create cell right in heightForRowAtIndexPath and store it in cache, then this cached cell will be returned by cellForRowAtIndexPath method.
Another problem is how to force cell to layout its subviews before it is used. It can be done if we temporary add cell to the tableView and update cell's frame with tabelView's width. After that all subviews will be layouted in the right way. Here is how I got it working in my TableKit library.
I've written up my learnings and solution for calculating the UITableViewCell height based on an inner UITextView on my blog. The post contains the code that works for universal apps, both table view styles and autorotation.
I edit TomSwift response which is the best way to do it :
here is my cell code (in swift)
class CommentaireTextViewCell: UITableViewCell,UITextViewDelegate {
#IBOutlet weak var textViewCom: UITextView!
weak var parentTableView:UITableView?
#IBOutlet weak var constraintTextViewTopMargin: NSLayoutConstraint!
#IBOutlet weak var constraintTextViewHeight: NSLayoutConstraint!
#IBOutlet weak var constraintTextViewBottomMargin: NSLayoutConstraint!
override func awakeFromNib() {
self.textViewCom.delegate = self;
}
func textViewDidChange(textView: UITextView) {
self.parentTableView?.beginUpdates();
self.parentTableView?.endUpdates();
}
func getHeight() -> CGFloat
{
constraintTextViewHeight.constant = self.textViewCom.contentSize.height;
// cell height = textview marge top+ textview height+ textView Marge bottom
return constraintTextViewTopMargin.constant+constraintTextViewHeight.constant+constraintTextViewBottomMargin.constant+8
// add 8 because it seems to have an inset inside the textView
}
override func setSelected(selected: Bool, animated: Bool) {
self.textViewCom.becomeFirstResponder();
}
}
for the explaination, the cell table view is a weak propertie of the cell so I can tell it to update layout when user enter text.
The height of the cell is the sum of its top constraint to the contentView, its bottom constraint to the contentView and the textView.contantSize.height which is also equal to the textView constant height
You need to return the correct height in the heightForRowAtIndexPath delegate method.
Try the following code:
CGSize constraintSize;
constraintSize.height = MAXFLOAT;
constraintSize.width = yourTextView.frame.size.width;
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"yourFontName" size:yourFontSize], NSFontAttributeName,
nil];
CGRect frame = [yourTextView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize stringSize = frame.size;//The string size can be set to the UITableViewCell
you can get the UITextView size programmatically.According to the size,set the height of the cell using the following delegate
tableView:heightForRowAtIndexPath:
Guys this is really cool stuff but you can use tableview:didSelectRowForIndexPath: to add the UITextView to the cell as it's subview when the user taps it, works very well since you need to use only 1 UITextView which you can reuse and won't interfere with the tableview receiving touches! Release it when done.
Details? just ask!