Custom UITableView Dynamic Cell Height - iphone

I have search and searched through endless blogs and articles on how to determine a dynamic height for a custom UITableViewCell and its detailed text. I have really had a hard time finding any good documentation on this.
What I need to do is have the cell grow according to the the text inside but never go below a height of 70.
I have tried several of the answers for this type of question here on StackOverflow but none of them worked. My whole app is just about finished but I really need to get this accomplished before I release and its troublesome.
Here is what I am trying but I just get a slop of cells overlapping each other. From what I read I need to find the frame if the custom cell or textview in the cell as well but I am not sure how to do that or mix them all together to return one height.
Any Help would be greatly appreciated Thank you!
- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath
*) indexPath
{
CGSize aSize;
aSize = [[(Tweet*)[tweets objectAtIndex:indexPath.row]tweet] sizeWithFont:[UIFont
systemFontOfSize:14]
constrainedToSize:CGSizeMake(300.0, 1000.0)
lineBreakMode:UILineBreakModeTailTruncation];
return aSize.height;
}

I had a similar issue a while back and this helped me tremendously.
#define PADDING 10.0f
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = [self.items objectAtIndex:indexPath.row];
CGSize textSize = [text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(self.tableView.frame.size.width - PADDING * 3, 1000.0f)];
return textSize.height + PADDING * 3;
}

Hey there so you are going to need to store the list of strings in an NSArray and then you are going to need to calculate the height of the nsstring using sizeWithFont:constrainedToSize: the documentation is found here http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
so your tableView method should look something like
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont fontWithName:"Helvetica" size:9] forKey: NSFontAttributeName];
CGSize cell_size = [string boundingRectWithSize:CGSizeMake(300,999)
options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
attributes:stringAttributes context:nil].size;
if (cell_size.height > 70)
{
return cell_size.height;
}
else
{
return 70;
}
}
EDIT : THIS has been updated for iOS 7

I tried many solutions, but the one that worked was this, suggested by a friend:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int height = [StringUtils findHeightForText:yourLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:17.0f]];
height += [StringUtils findHeightForText:yourOtherLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:14.0f]];
return height + CELL_SIZE_WITHOUT_LABELS; //important to know the size of your custom cell without the height of the variable labels
}
The StringUtils.h class:
#import <Foundation/Foundation.h>
#interface StringUtils : NSObject
+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font;
#end
StringUtils.m class:
#import "StringUtils.h"
#implementation StringUtils
+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font {
CGFloat result = font.pointSize+4;
if (text) {
CGSize size;
CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil];
size = CGSizeMake(frame.size.width, frame.size.height+1);
result = MAX(size.height, result); //At least one row
}
return result;
}
#end
It worked perfectly for me. I had a Custom Cell with 3 images with fixed sizes, 2 labels with fixed sizes and 2 variable labels. Worked like a charm. Hope it works for you too.
Best regards, Alexandre.

iOS 7
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
NSString *text = #"Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello HelloHello HelloHello HelloHello HelloHello HelloHello Hello";
CGRect rect = [text boundingRectWithSize:CGSizeMake(280.0f, CGFLOAT_MAX) options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:#{
NSFontAttributeName: [UIFont fontWithName:#"Hellvetica" size:14.0f]
} context:nil];
return CGSizeMake(320.0f, ceil(rect.size.height) + 10.0f);
}

I just wrote about this problem and how I finally decided to solve it. You can read about it here: Dynamic UITableView Cell Height Based on Contents
Basically, I created a UITableView subclass that automates the handling and calculation of dynamic cell height for both default and custom cells. It is not a silver bullet and probably needs to be extended and tweaked, but I have used it as is in several apps with good result.
You can grab the code here: https://github.com/danielsaidi/AutoSizeTableView
Hope it helps!
(...and if it didn't, I'd love to hear why not)

Use UITableViewDelegate method:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// here you need to get height of your textlabel in your custom cell.
MyCustomCell *myCell = (MyCustomCell *)[tableView cellForRowAtIndexPath:indexPath];
return myCell.myTextLabel.frame.size.height + 20;
}
Something like this

Related

Detect Distance between UITableView Section Headers

In a plain UITableView with custom UIView's as section headers, is there a way to calculate:
When one of the Section is on the top, the distance between that section and the next one that would come?
I am expecting to calculate this here:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
You can find the number of rows in that section by calling the tableView:numberOfRowsInSection: method from the UITableViewDataSourceDelegate protocol. You can get the height for each row in the section with the tableView:heightForRowAtIndexPath: method from the UITableViewDelegate protocol. Add up the height for all the rows and you have the distance you want.
Your code would look something like this, assuming you have a reference to the tableview and the section.
float totalHeight = 0;
for (int i = 0; i < [tableViewDataSourceDelegate
tableView:tableView
numberOfRowsInSection:section]; i ++) {
totalHeight += [tableViewDelegate
tableView:tableView
heightForRowAtIndexPath:[NSIndexPath
indexPathForRow:i
inSection:section]];
}
Haven't had a chance to test this code, but it Should Work[tm].
Edit
This will only work if the header is at the top.
(assuming all rows are same height)
NSIndexPath *topCellIndexPath = [tableView indexPathsForVisibleRows][0];
UITableViewCell *topCell = [tableView cellForRowAtIndexPath: topCellIndexPath];
CGFloat distanceToNextSection = [tableView convertRect: [topCell frame] fromView: topCell.superview].origin.y - tableView.contentOffset.y + ([self tableView: tableView numberOfRowsInSection: topCellIndexPath.section] - topCellIndexPath.row)*tableView.rowHeight
I was able to solve this by doing the following:
Before creating an section header, check if you have the section header for a given section. If you do return it from the NSMutableArray. If not keep going.
When you create the section header, keep a reference to it in a NSMutableArray.
When you scroll in:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
Do the following:
// Get the toppest section
NSUInteger sectionNumber = [[self.tableView indexPathForCell:[[self.tableView visibleCells] objectAtIndex: 0]] section];
// Get a reference to it
SFBasicSectionHeader *topHeader = [arrayOfWeakHeaders objectAtIndex:sectionNumber];
SFBasicSectionHeader *bellowHeader;
// Check if it's Ok to get the bellow header
if (sectionNumber+1<[arrayOfWeakHeaders count] && [arrayOfWeakHeaders objectAtIndex:sectionNumber +1])
{
bellowHeader = [arrayOfWeakHeaders objectAtIndex:sectionNumber+1];
}
The difference between both will be:
CGFloat differenceBetweenTopAndBellowSection = bellowHeader.frame.origin.y - topHeader.frame.size.height - self.tableView.contentOffset.y;
Done.

UICollectionView Custom Layout - Cells are not properly arranged

In my iPhone app,I want to use custom lay out in UICollectionview.There will different heights for Collection view cells.I want to set a lay out like that.
I tried many things and at last,made that height adjustment.(I am getting height for each object from server)
But the layout is not in a proper way.There are lots of spaces in between the items.
How can I achieve a proper layout.
Now I am getting a view like this
What I want is -
Here is my code -
-(CGFloat)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath*)indexPath
{
return 60;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return array.count; // getting from server
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.imageBG.image = [imagearray objectAtIndex:indexPath.item];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize retval;
// I'm taking height of each images into HeightArray
retval = CGSizeMake(100, [[HeightArray objectAtIndex:indexPath.item]floatValue ]+50);
return retval;
}
From your screenshots it seems you are trying to use standard UICollectionViewFlowLayout (or its subclass). Unfortunately its limitation is that each row will always occupy the same height - it will be the same as in highest cell in that row, so you it is not suitable for your task and you have to implement your own layout.
Check UICollectionViewWaterfallLayout class which, although has its limitations (supports only 1 section, no decoration views) provides layout you need and at least can be a good starting point if you won't be able to use it as it is now.

Expanded UITableViewCell according to UILabel size

I have a UITableView with 4 UILabel's: Title, Body, Author and Date, he looks like this:
What I want to accomplish is, when user click on the cell itself, another label should be added to the cell, the "Body" label and the cell should expand according to this label size.
Something like this:
How can I do that? I've searched stackoverflow, tried some code pieces, but still didn't found the right solution.
Thanks!
Edit 1: 14.11.12 at 14:52
I managed to change the size of the UILabel with the current text:
- (CGRect )resizeLabelByFontSize:(UILabel *)customCellLabel withMaxHeightSize:(CGFloat )maxHeight
{
CGSize maximumLabelSize = CGSizeMake(239, maxHeight);
CGSize expectedLabelSize = [customCellLabel.text sizeWithFont:customCellLabel.font constrainedToSize:maximumLabelSize lineBreakMode:customCellLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = customCellLabel.frame;
newFrame.size.height = expectedLabelSize.height;
return newFrame;
}
But how can I change the size of the cell according to the size of the new UILabel?
By seeing Images in Question
Here is the method which just create the Dynamic FRAME for UILabel have a look at this
By getting the Height and Width for UIlabel you can calculate the Whole height and could set the Row Height of UITableView.
- (void)setLabeltextWithVerticalAlignTop:(NSString *)theText
{
CGSize labelSize;
// here labelSize is hard-wired but could use constants to populate the size
labelSize = CGSizeMake(210, 129);//this is just for example
//now create the Size from textString SO that We could assign this size to the Label.
CGSize theStringSize = [theText sizeWithFont:lblTitle.font constrainedToSize:labelSize lineBreakMode:lblTitle.lineBreakMode];
lblTitle.frame = CGRectMake(lblTitle.frame.origin.x, lblTitle.frame.origin.y, theStringSize.width, theStringSize.height);
lblTitle.text = theText;
}
Call Above Method For setting the height and Width of description Label you need to pass the text to be shown on that description label.
As you gets the height for that Label, Now On the Basis of this You can Adjust the heigh of Row of TableView.
EDIT:Above Code Just Create the Dynamic Frame For The UILabel
You should take a view of this this is what you looking for....!!!.here you would find a sample code too.
EDIT:As you edited your Question see ,it just the logic which you need to convert it into runnable code here it is.
Use Below Method in Your Code called for each row, and make some calculation inside it.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat rowHeight=0.0;
//here it seems cell have 4 subview added on it.
//so if you could calculate the totla hieht of them.
//so what you really need to do.you just use hieght calculative Method for getting hieght of each of three UILabel
//you need to modify `setLabeltextWithVerticalAlignTop` method .
rowHeight= [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];// suppose it returns some hieght for FisrtLabel.
//suppoose here you get the 20.0 height here
rowHeight= rowHeight+[self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for secondUIlabel.
//suppoose here you get the 40.0 height here
rowHeight= rowHeight+ [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for ThirdUIlabel.
// suppoose here you get the 15.0 height here
//here you have totla height you just need to add some gapping floating value for all of three UIlabel.so that the could not overlap like as.
rowHeight= rowHeight+20.0;
//now you can return that total height
return rowHeight;
}
Note:This is just logic you need to convert it into runnable code.i am sure this can help.
I hope it may help you.
Implement the following methods
– (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// cast cell, add label, expand labels etc
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [indexPath isEqualTo:[tableView indexPathForSelectedRow]] ? /* expanded height */ : 80 /* normal height */;
}
If you want the row to stay selected even after another row is selected then add a custom BOOL property to your custom cell, e.g. expanded, and use that to determine the height.
You can use tableView:didSelectRowAtIndexPath
In that method you can then create your code to unhide the body label, adjust the relative positions of everything else. Calculate the new height of the row and then call the Table View's reloadRowsAtIndexPath: withRowAnimation: method.
Sorry if there's not a lot of detail in that, but hopefully that should get you on the right track.
Ok, firstly... To expand you need something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
Now here is the catch:
You should calculate the size of the UITableViewCell (expanded and non-expanded)
Doing so when you are actually scrolling might be expensive and will give you a bad experience
My advice:
Calculate both sides, before you have actually conclude to build the UITableView, since you want to have dynamic sizes. If you don't and all cells will have the same size expanded, you can use what lammmert said.
NSIndexPath *selectedRow;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
selectedRow = indexPath;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath == selectedRow){
//return your custom value
}
return 100;
}
I think it will look something like that
So, in order to do this, using expended UITableViewCell, i've created 2 different custom cells, at start the table is showing the first cell, when I click on the cell, the table is showing the second one. Its that easy - yeah!
So I have the UIViewController with the UITableView that implements the table delegate methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self.selectedCellIndexPath isEqual:indexPath])
{
return [self expandedCellHeight:indexPath];
}
else
{
return kRegularCellHeight;
}
}
-(CGFloat)expandedCellHeight:(NSIndexPath *)indexPath
{
CGSize maxSize = CGSizeMake(303, 200);
NSString* bodyText = [[self.data objectAtIndex:indexPath.row] objectForKey:kForumMessagesBody];
CGSize fitSize = [bodyText sizeWithFont:[UIFont systemFontOfSize:13] constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = 384 - 69 + fitSize.height;
NSLog(#"expandedHeight: %f",height);
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Answer cell
if ([self.selectedCellIndexPath isEqual:indexPath])
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell expandedAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell expandedAnswerCell];
}
self.expandedCell = cell;
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell reqularAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell regularAnswerCell];
}
}
cell.labelMedia.text = [self.data objectAtIndex:indexPath.row];
return cell;
}
I also have custom cell, the class called ForumCell.h and ForumCell.m and it has 2 different XIB files: ForumRegularAnswerCell.xib and ForumExpandedAnswerCell.xib, I have the following code inside ForumCell.h:
+ (NSString*)reqularAnswerReuseIdentifier
{
return #"RegularAnswerCellReuseIdentifier";
}
+ (NSString*)expandedAnswerReuseIdentifier
{
return #"ExpandedAnswerCellReuseIdentifier";
}
+ (ForumCell*)regularAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumRegularAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
+ (ForumCell*)expandedAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumExpandedAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self)
{
_originalCellHeight = self.frame.size.height;
_originalLblBodyHeight = self.lblBody.frame.size.height;
}
return self;
}
You can also use more than 2 xibs if you'd like its up to you. but this is the basics.
Enjoy!

Table cell last line being cutoff when resizing using NSString sizeWithFont:constrainedToSize:lineBreakMode:

When using the following code to re-size a table row the last line of text is always cutoff, no matter how many lines there are. But there is white space added that looks like enough space for the text.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
CGFloat restOfTheCellHeight = tableView.rowHeight - cell.detailTextLabel.frame.size.height;
CGSize constrainedSize = CGSizeMake(cell.detailTextLabel.frame.size.width, CGFLOAT_MAX);
CGSize textHeight = [cell.detailTextLabel.text sizeWithFont:cell.detailTextLabel.font constrainedToSize:constrainedSize lineBreakMode:cell.detailTextLabel.lineBreakMode];
CGFloat newCellHeight = (textHeight.height + restOfTheCellHeight);
if (tableView.rowHeight > newCellHeight) {
newCellHeight = tableView.rowHeight;
}
return newCellHeight;
}
Here is the code in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCellTableRowTypeSingleLineValueSmallLabel *cell = (CustomCellTableRowTypeSingleLineValueSmallLabel *)[tableView dequeueReusableCellWithIdentifier:#"CellTypeMultiLineLabelInCellSmallCell"];
if (cell == nil) {
NSArray *xibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCellTableRowTypeSingleLine" owner:nil options:nil];
for(id currentObject in xibObjects) {
if([currentObject isKindOfClass:[CustomCellTableRowTypeSingleLineValueSmallLabel class]]){
cell = (CustomCellTableRowTypeSingleLineValueSmallLabel *)currentObject;
}
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.detailTextLabel.numberOfLines = 0;
cell.detailTextLabel.text = self.attributeStringValue;
cell.textLabel.text = self.rowLabel;
return cell;
}
Any ideas?
You need to call [cell.detailTextLabel sizeToFit] in order for the label to actually resize in cellForRowAtIndexPath. It will not resize on its own just because you set numberOfLines to 0. See this question and read its answers for more clarification.
You are calculating the cell height appropriately in your heightForRowAtIndexPAth method, but then in your cellForRowAtIndexPath method you are never actually using it to set the height of your label within it.
So the table is allocating the right amount of space based on your heightForRowAtIndexPath, but then inserting into that space the unresized cell that you return from cellForRowAtIndexPath. I think this might the the cause of the problem and would explain the results you are seeing.
In cellForRowAtIndexPath you need to actually set the height of the label using the same calculation.
i.e.
CGSize constrainedSize = CGSizeMake(cell.detailTextLabel.frame.size.width, CGFLOAT_MAX);
CGRect cframe = cell.detailTextLabel.frame;
cframe.size.height = constrainedSize.height;
cell.detailTextLabel.frame = cframe;
You may also need to actually set the content view frame as well (not sure how it works with a non-custom cell).
I'm also not sure its a good idea to be calling cellForRowAtIndexPath from the heightForRowAtIndexPath method (it would probably be better to just directly access the text data you are using for the size calculation directly).
Turns out I just needed to enable all of the Autosizing options in interface builder for the label.

uitableviewcell textlabel too long and push detailtextlabel out of view

When i'm using UITableViewCellStyleValue1, i got a long string of textLabel, and somehow the detailTextLabel got push out from the view.
When i shorted my textLabel text, then i can see the detailTextLabel's text.
Is there anyway to limit the width of textLabel in the above style so that it will truncate the textLabel with it's too long?
My code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.lineBreakMode = UILineBreakModeTailTruncation;
//---get the letter in each section; e.g., A, B, C, etc.---
NSString *alphabet = [self.currencyNameIndex objectAtIndex:[indexPath section]];
//---get all states beginning with the letter---
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
self.currencyList = [self.keyCurrencyName filteredArrayUsingPredicate:predicate];
if ([self.currencyList count] > 0)
{
NSString *currencyName = [self.keyCurrencyName objectAtIndex:indexPath.row];
cell.textLabel.text = currencyName;
NSString *currencyCode = [self.valueCurrencyCode objectAtIndex:indexPath.row];
cell.detailTextLabel.text = currencyCode;
}
return cell;
}
so my currency name will be a long one on some entry.
Simplest for me was to subclass UITableViewCell and override the layoutSubviews.
Couldn't find a reliable way to calculate the positions from just the label frames so just hardcoded the accessory width for in this case a UITableViewCellStyleValue1 cell with a UITableViewCellAccessoryDisclosureIndicator accessory type.
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat detailTextLabelWidth = [self.detailTextLabel.text sizeWithFont:self.detailTextLabel.font].width;
CGRect detailTextLabelFrame = self.detailTextLabel.frame;
if (detailTextLabelFrame.size.width <= detailTextLabelWidth && detailTextLabelWidth > 0) {
detailTextLabelFrame.size.width = detailTextLabelWidth;
CGFloat accessoryWidth = (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) ? 28.0f : 35.0f;
detailTextLabelFrame.origin.x = self.frame.size.width - accessoryWidth - detailTextLabelWidth;
self.detailTextLabel.frame = detailTextLabelFrame;
CGRect textLabelFrame = self.textLabel.frame;
textLabelFrame.size.width = detailTextLabelFrame.origin.x - textLabelFrame.origin.x;
self.textLabel.frame = textLabelFrame;
}
}
#Jhaliya #lucas
cell.textLabel.numberOfLines = 3; // set the numberOfLines
cell.textLabel.lineBreakMode = UILineBreakModeTailTruncation;
see here: Custom UITableViewCell. Failed to apply UILineBreakModeTailTruncation
I ran into a similar problem when trying to use "Right Detail" in UITableView; the right detail's built in title label was clobbering my subtitle label.
Eventually I gave up on the "Right Detail" in favor of my own custom one (using swift and autolayout):
I created my own simple class that inherited from the UITableViewCell:
class TransactionCell: UITableViewCell
{
}
I set my prototype cell use that custom class by setting the "Style" field to "Custom" on the "Table View Cell" Menu and by adding "TransactionCell" to the "Class" field of the "Custom Class" menu. These menus are available when you select the prototype cell in the storyboard.
I added two labels to my prototype cell and connected them to my custom class by right click dragging from my labels to my class (Oddly, I had to clean my build before it would let me do this):
class TransactionCell: UITableViewCell{
#IBOutlet weak var detailsLabel: UILabel!
#IBOutlet weak var amountLabel: UILabel!
}
I added new constraints to my labels, taking advantage of swift's autolayout features (You will need to set these to match your own requirements; see a tutorial on autolayout if you are stuck)
...and set the "Lines" and "Line Breaks" fields in the respective "Label" menus so that the spacing between labels would be even and so that my details label could flex to multiple lines.
It worked for me, allowing me to have the flexibility of different amounts of multiple lines in a UITableView in swift per cell, while formatting the word wrapping so that it looked nice and even, like I would have expected the "Right Detail" to do automatically.
I had the same problem and had to create a UITableViewCell subclass. It's easier to do that than I thought:
Basically, just make a new file, that is a subclass of UITableViewCell
Add the labels and synthesize them:
// in the .h file
#property (nonatomic, weak) IBOutlet UILabel *textLabel;
#property (nonatomic, weak) IBOutlet UILabel *detailTextLabel;
// in the .m file
#synthesize textLabel, detailTextLabel;
In the StoryBoard, set your class as the cell's class, make the Style "Custom" and add two labels in the cell to look exactly as you want (I made them look the same as the default: http://cl.ly/J7z3)
The most important part is to make sure you connect the labels to the cell
You need to control-click from the Cell to the label in the Document outline. Here's a picture of what it looks like: http://cl.ly/J7BP
What helped me to understand how to create custom cell, dynamic cells, and static cells is this youtube video: http://www.youtube.com/watch?v=fnzkcV_XUw8
Once you do that, you should be all set. Good luck!
Swift version supporting the latest iOS (12):
override func layoutSubviews() {
super.layoutSubviews()
guard let detailWidth = detailTextLabel?.intrinsicContentSize.width, var detailFrame = detailTextLabel?.frame else {
return
}
let padding = layoutMargins.right
if (detailFrame.size.width <= detailWidth) && (detailWidth > 0) {
detailFrame.size.width = detailWidth
detailFrame.origin.x = frame.size.width - detailWidth - padding
detailTextLabel?.frame = detailFrame
var textLabelFrame = textLabel!.frame
textLabelFrame.size.width = detailFrame.origin.x - textLabelFrame.origin.x - padding
textLabel?.frame = textLabelFrame
}
}
Adjust the frame of the view: textLabel
CGRect aFrame = cell.textLabel.frame;
aFrame.size.width = 100; // for example
cell.textLabel.frame = aFrame;
Updated Gosoftworks Development`s answer.
Swift 3
class BaseTableViewCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
guard let tl = textLabel, let dl = detailTextLabel else { return }
if (tl.frame.maxX > dl.frame.minX) {
tl.frame.size.width = dl.frame.minX - tl.frame.minX - 5
}
}
}
Create a custom UITableViewCell with a UILabel in it that you can control however you want, or truncate the text that you assign to the base-class textLabel to fit the space that you have.
It's not perfect, but I have used the text-truncation technique in places where a custom cell is overkill (ex. when the only issue was fitting the text in) using an NSString category with a method similar to the following:
- (NSString *)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font
{
NSString *result = [NSString stringWithString:self];
while ([result sizeWithFont:font].width > width)
{
result = [result stringByReplacingOccurrencesOfString:#"..." withString:[NSString string]];
result = [[result substringToIndex:([result length] - 1)] stringByAppendingString:#"..."];
}
return result;
}
Probably not 'optimized' but it works for simple scenarios.
1st: set line break mode
textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
2nd: set texLabel frame width you want (e.g. 200)
CGRect textFrame = self.textLabel.frame;
CGRect newTextFrame = CGRectMake(textFrame.origin.x, textFrame.origin.y, 200, textFrame.size.height);
self.textLabel.frame = newTextFrame;
It works!! But i just changed Christopher King`s code:
- (NSString *)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font :(NSString*) result
{
while ([result sizeWithFont:font].width > width)
{
result = [result stringByReplacingOccurrencesOfString:#"..." withString:[NSString string]];
result = [[result substringToIndex:([result length] - 1)] stringByAppendingString:#"..."];
}
return result;
}
and usage:
NSString* text = #"bla bla bla some long text bla bla";
text = [self stringByTruncatingToWidth:cell.frame.size.width-70.0 withFont:[UIFont systemFontOfSize:17] :text];
cell.textLabel.text = text;
I've struggled with this a bunch and found a pretty simple answer. My textLabel was on the left and would push out the detailText on the right to the point you couldn't see it at all sometimes.
My solution, change the Table View Cell style to Subtitle from Left Detail or Right Detail. This solution works if you don't mind your detailText being below instead of on the right or left.
If you are having issues with the height of the row, you can adjust that using the code below in viewDidLoad.
self.tableView.estimatedRowHeight = 500 // set this as high as you might need, although I haven't tested alternatives
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.reloadData()
Use the UILabel lineBreakMode property to restrict your text within the width of your UILabel
#property(nonatomic) UILineBreakMode lineBreakMode
Use it as below.
myLabel.lineBreakMode = UILineBreakModeTailTruncation;
Here is the list of values which could be used with lineBreakMode.
http://developer.apple.com/library/ios/#documentation/uikit/reference/NSString_UIKit_Additions/Reference/Reference.html#//apple_ref/doc/c_ref/UILineBreakMode
EDITED:
Set the width of your UILabel as per your requirment
eg.
myLabel.frame.size.width = 320;