UILabel dynamic width with sizeToFit on a cell - iphone

I have a custom UITableViewCell with a UILabel inside on the Storyboard. I set the text of this UILabel dynamically and I need to resize the width of the UILabel accordingly because after it I have another UILabel that must stay side to side.
I am trying with
> MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
> author = #"Some dynamic text";
> cell.authorLabel.text = author;
> [cell.authorLabel sizeToFit];
What happens:
at the first render the UILabel just keeps the width that is set on the storyboard, thus either the text is cropped or there is too much space after it.
After scrolling, the cells are dequeued and the sizeToFit method is applied properly, but is too late because the second UILabel is already positioned on the wrong place.
(cannot post picture due to low reputation restriction)
Any idea?

maybe you mean something like that:
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
author = #"Some dynamic text";
cell.authorLabel.text = author; <-- excluded in your code
[cell.authorLabel sizeToFit];
If this doesn't work, try to use some like this:
subclass UITableViewCell and rewrite the method below:
- (void)setSelected:(BOOL) selected
{
[super setSelected:selected];
[self.authorLabel sizeToFit];
}

use this one for getting dynamic height and width,
CGSize sizeDynamic = [str sizeWithFont:[UIFont fontWithName:#"Arial-BoldMT" size:14] constrainedToSize:CGSizeMake(CGFLOAT_MAX,CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
you can get,
sizeDynamic.height and sizeDynamic.width values

Related

iPhone - Create custom UITableViewCell top and bottom borders

I have been looking everywhere and have not quite found my answer.
I populating a UITableView with dynamic cells from JSON and I am trying to hide any extra cells. I turned off the separators in IB, and of course all the cell separators disappear. How do I add a line to the bottom and top of each tableviewcell so that only the cells that have information show a border? I have imported Quartz and have been playing with CALayer but can't find a solution.
I found a similar question here, but the only answer was not very helpful.
What would be a better, different way of doing this?
Here are my cellForRowAtIndexPath and my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//set equal to the information in the array
return [_jsonDataArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//create Dictionary of data in row
NSDictionary *jsoninfo = [_jsonDataArray objectAtIndex:indexPath.row];
//get required keys from dictionary and assign to vairables
NSString *title = [jsoninfo objectForKey:#"title"];
NSString *subtitle = [jsoninfo objectForKey:#"subtitle"];
NSURL *imageURL = [NSURL URLWithString:[jsoninfo objectForKey:#"series_image_URL"]];
//download the images.
NSData *imgData = [NSData dataWithContentsOfURL:imageURL];
UIImage *img = [[UIImage alloc] initWithData:imgData];
//set boarder for custom cells... I need to have a border on the top and bottom of the cells I am creating so xcode does not autofill the empty space.
//fill in text to cells
cell.textLabel.text = title;
cell.detailTextLabel.text = subtitle;
cell.imageView.image = img;
return cell;
}
I also think it's not the best idea, but if you really want to do this, here's code that will achieve what you want:
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// Draw top border only on first cell
if (indexPath.row == 0) {
UIView *topLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 1)];
topLineView.backgroundColor = [UIColor grayColor];
[cell.contentView addSubview:topLineView];
}
UIView *bottomLineView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.bounds.size.height, self.view.bounds.size.width, 1)];
bottomLineView.backgroundColor = [UIColor grayColor];
[cell.contentView addSubview:bottomLineView];
Put this code in the tableView:cellForRowAtIndexPath: method. The final look of your UITableView will be like this:
Take into account that this is not very good for performance, especially if you have a lot of cells. If you have a bigger amount of data, refer to this SO question for help on how to optimize the drawing.
Only try this at tableView:cellForRowAtIndexPath: method
[cell.contentView.layer setBorderColor:[UIColor grayColor].CGColor];
[cell.contentView.layer setBorderWidth:1.0f];
It sounds like a sub-optimal solution to try to "distinguish" your valid cells from empty ones with lines. A superior approach would be to clean up the data source before populating the table with it.
This is not the answer to the question, but the clean solution for the original problem.
Add an empty UIView as the footer of the UITableView. Then the empty cells will be hidden. See this answer.
Use Custom Cells. Your Datasoure (Models) should drive the information into the Custom Cells. Create a setter within the Custom Cell class that can be set at each row. as in....
Allocation your Custom Cell with reuse Id,
Pass the property that is determing if the line should show:
[cell setCustomLines:Model.property];
return the cell;
You will have far more flexibility to design the CustomCell any way you want, Images, Lines, Colors, or other ways of letting your user's see a difference among your cells.
Technically, Marco's Answer will work, and good job on a simple solution. But you will not be able to expand this very much farther this this.

set width of UITableViewCell text

I have a uitableview with uitableviewcells however some of my text is so long its going right to the edge of the cell, where my accessory tick view is going to be when selected..
I am currently wrapping the text if it extends beyond the width and it goes onto the second line.. However I was hoping there is a nice way to restrict the width of this UITableViewCell label.
Inside your cellForRowAtIndexPath: function. The first time you create your cell:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
}
Now specify how large your UITableViewCell will be, so do that in your heightForRowAtIndexPath function:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *str = [[arrTexts objectAtIndex:indexPath.row]; // filling text in label
CGSize maximumSize = CGSizeMake(300, 100); // change width and height to your requirement
CGSize strSize = [str sizeWithFont:[UIFont fontWithName:#"Helvetica" size:17] constrainedToSize:maximumSize lineBreakMode:UILineBreakModeWordWrap] //dynamic height of string depending on given width to fit
return (10+strSize.height+10) // caculate on your bases as u have string height
}
In cellForRowAtIndexPath Set the width of the textLabel
cell.textLabel.frame = CGRectMake(cell.textLabel.frame.origin.x, cell.textLabel.frame.origin.y, 280, cell.textLabel.frame.size.height);
thats all.
I think you need subclass UITableViewCell to override -layoutSubviews... call super, then frame your label appropriately. Constraints my be of some help, but I haven't used them on iOS yet. (Assuming the problem is the built-in layout function make the label as wide as it needs to be to accommodate your text)
There are two ways to do so.
1) Create a custom UITableViewCell class, add your UILabels with whatever position & size you want. And then use it in cellForRowAtIndexPath method.
2) Else, don’t use default UILabels of UITableViewCell ie. textLabel & detailTextLabel, add your own UILabels in cell with whatever position & size you want.

Trying to calculate frame size of UILabel based on the amount of text for subview of cell

The code below has code that determines the frame size of a UILabel, and I think it does work, however when I place it within my rowAtIndexPath for a UItable I get wonky results.
Perhaps, I dont fully understand how or what the reuseIdentifier does, but I placed the code to calculate the frame only when the cell is nil. What happens is that the heights are calculated only for the first three cells, then it repeats in sequence for the rest of the cells. For example, cell one's height is used for cell four's height.
Maybe someone can point me in the right direction as to how I should setup my calculations.
Thanks!
if(cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:DisclosureButtonCellIdentifier] autorelease];
//start adding custom subviews to the table cell
//addSubview for Description
UILabel *descValue = [[UILabel alloc] init];
NSString *descString = rowData.summary;
CGSize maximumSize = CGSizeMake(185, 130);
UIFont *descFont = [UIFont fontWithName:#"HelveticaNeue" size:12];
CGSize descStringSize = [descString sizeWithFont:descFont
constrainedToSize:maximumSize
lineBreakMode:descValue.lineBreakMode];
CGRect descFrame = CGRectMake(125, 60, 185, descStringSize.height);
descValue.frame = descFrame;
descValue.backgroundColor = [UIColor redColor];
descValue.font = descFont;
descValue.tag = kDescriptionValueTag;
descValue.lineBreakMode = UILineBreakModeWordWrap;
descValue.numberOfLines = 0;
[cell.contentView addSubview:descValue];
[descValue release];
}
UILabel *desc = (UILabel *)[cell.contentView viewWithTag:kDescriptionValueTag];
desc.text = rowData.summary;
Using NSString UIKit Additions, you can get the width of a string using a particular font:
[myString sizeWithFont:myFont];
There are several other functions in that Additions set that can help figure out how big a piece of text will be, with different layout options, for single and multi-line text, etc.
The purpose of the reuseIdentifier is to let you reuse a cell -- with particular subviews in particular places -- without the device having to spend the execution time to do all that layout. Definitely more useful back in the iPhone 1 days when the processor was much slower. Until you are more familiar with reuseIdentifiers, I would suggest you just create a new cell every time that function is called.
Each time the OS calls your cellForRowAtIndexPath, you need to fill out the content correctly. Anything that needs to get resized or changed depending on the row should be set.
Frankly, I did not try understanding your code completely. That is nearly impossible especially because you close the method with brackets without returning anything but cellForRowAtIndexPath which I assume you are referring to, requires the return of a UITableViewCell object.
Apparently we are looking only at some fraction of the code.
However, layouting a cell properly is not the full task. You need to tell the table the height of each cell.
You may want to implement
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
for that task. That method is good if the cell height varies.
If the cell hight is different from the standard but does not vary from cell to cell then setting of the rowHeight property of the tableView will be sufficient.
I think that in your case the implementation of the method heightForRowAtIndexPath is mandatory.
BTW, you may want to look at subclassing the UITableCellView and implement the layoutSubviews method in your subclass.
You didn't show all of your code but usually to implement tableView:cellForRowAtIndexPath: you start with the following:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// get a cell from the queue of reusable cells, if any
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: DisclosureButtonCellIdentifier];
if (cell == nil) {
// No cell to reuse, need to create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:DisclosureButtonCellIdentifier] autorelease];
// add cell subviews here
}
// fill in cell content and resize subviews here
...
return cell;
}
Once you have created 3 (in your case) cells they get reused as the table scrolls so you don't have to keep creating cells. The call to dequeueReusableCellWithIdentifier will return one of the previously created cells if it was not longer in use (it scrolled off the top or the bottom).

How to make a UITableViewCell with a UITextView inside, that dynamically adjust its height, on the basis of the UITextView?

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!

iPhone Development - UITableViewCellStyleValue2

Note: I'm using iPhone SDK 3.0 Final
Version for Leopard.
I need to prepare a table listing similar to the one used by iPhone's Photo Album application. There are two new styles for UITableViewCell:
UITableViewCellStyleValue1
UITableViewCellStyleValue2
I thought i can use these instead of creating a custom cell class, but looks like they don't work properly. I'm facing following issues with UITableViewCellStyleValue2:
I'm unable to show an image.
If i change the font size of textLabel and detailTextLabel, the values are not aligned.
Here's the sample code:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.image = [UIImage imageNamed:#"Pointy.gif"];
cell.textLabel.text = #"Label";
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.font = [UIFont systemFontOfSize:21];
cell.detailTextLabel.text = #"(10)";
cell.detailTextLabel.textColor = [UIColor grayColor];
cell.detailTextLabel.font = [UIFont systemFontOfSize:17];
return cell;
}
This piece of code work with UITableViewCellStyleDefault and UITableViewCellStyleSubtitle to show images. Does UITableViewCellStyleValue2 support cell image property? I see blank space in place of image area (left corner of the cell). Am i doing something wrong here?
In case this doesn't work, how can i build a custom cell class with a style similar to UITableViewCellStyleValue2 style. I'm particularly interested in getting the detailTextLabel left aligned with textLabel.
Thanking in anticipation.
The new builtin styles layout sets of fields with configurations that are the same as a number of the system layouts (like the cells in the AddressBook).
As to your exact specific:
UITableViewCellStyleValue2 doesn't have an image
The builtin styles layout logic assume the font metrics are what they are set to. If you change those the fields won't line up
You can get a detailed decription of what is available from each of the builtin styles in the documentation. If none of those set your needs you need to subclass UITableViewCell, add your own subviews, and layout them out in layoutSubviews. Apple has A large number of examples of how to the TableViewSuite.