I have a custom class created for custom cells for my table view. There's UILabel for messages. I'm using storyboard to set the constraints, pinning the label to the top and right side of the table cell, I found that doing this causes the label to resize to fit the content. Now I can't figure out how to add padding to the label because anything done in cellForRowAtIndexPath doesn't work since auto layout is selected. I've seen many examples that do CGRectMake with float values but they don't work and I think it's because of auto layout. Is there any solution for this?
I'm not even sure what code to show for this...so here goes something:
How I'm getting the height of the message...the cell is set for 220 width...cellForRowAtIndexPath:
static NSString *cellIdentifier = #"chatCell";
ChatCell *cell = (ChatCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[ChatCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
info = [receivedData objectAtIndex:indexPath.row];
message = [info objectForKey:#"message"];
CGSize constraint = CGSizeMake(220, 2000);
CGRect rect = [message boundingRectWithSize:constraint options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:[UIFont systemFontOfSize:12.0f]} context:nil];
CGSize size = rect.size;
With that information, I set the table row heights in heightForRowAtIndexPath. But adjusting the frame of the label cell.messageLabel gives me no result. Here's something else I tried in the cellForRowAtIndexPath:
UIView *messageFrame = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 220, size.height)];
[messageFrame addSubview:cell.messageLabel];
[cell addSubview:messageFrame];
But this is the result in the simulator:
Everything is moved up and the whole text doesn't show. Is there a simple solution for this or do I have to rethink my entire code?
ANSWER:
What I did was add a UIView in storyboard and put it's constraints to the bottom of the cell. Since I was adjusting the cell height based on content bounds function from ios7, setting the constraint to the bottom automatically stretched the UIView all the way to the bottom. Playing with the message label width, I got to have padding. Hope this helps someone.
Related
I am building out a dynamic tableview cell. The height is dynamic based on the content that is loaded. I'm running into a hangup with height of the imageview. The images currently load aspect fill in an imageview that is 100% of the width. Is it possible to set an autolayout constraint on my imageview so that the imageview is hidden (has a height of 0) if there is no image for a tableview cell?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewTileCell *cell = [tableView dequeueReusableCellWithIdentifier:#"tileCell"];
if (!cell)
{
[tableView registerNib:[UINib nibWithNibName:#"TableViewTileCell" bundle:nil] forCellReuseIdentifier:#"tileCell"];
cell = [tableView dequeueReusableCellWithIdentifier:#"tileCell"];
}
cell.tileView.layer.borderColor = [UIColor blackColor].CGColor;
cell.tileView.layer.borderWidth = 1.0f;
[cell.contentView setBackgroundColor:[UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.0]];
NSString *url=[self.resultsArray[indexPath.row] valueForKey:#"imageURL"];
cell.tileTitle.text = [self.resultsArray[indexPath.row] valueForKey:#"title"];
cell.tileDate.text = [self.resultsArray[indexPath.row] valueForKey:#"date"];
cell.tileContent.text = [self.resultsArray[indexPath.row] valueForKey:#"summary"];
if(![url isEqualToString:#""]){ //If we got a url value back load the image
[cell.tileImageview setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
return cell;
}
Is it possible to set an autolayout constraint on my imageview so that the imageview is hidden (has a height of 0) if there is no image for a tableview cell?
Make the whole cell content a vertical UIStackView. In cellForRow, make the image view hidden. The stack view has the wonderful ability to change the constraints in exactly the way you describe: when the image view is hidden, the constraints will change and the image view will occupy zero space, with the other views occupying the whole space.
The alternative is not terrible: you simply have to do, yourself, what the UIStackView would do — remove the empty image view in code and adjust the constraints, in cellForRow. Swapping constraints and views together into and out of the interface is standard practice, and is easy to do.
I'm trying to get my UITableViewCell.textLabel property to autofit text both vertically and horizontally.
Right now my tableView is not scrollable, so the cell frame is not dynamic. I need the textLabel property to word wrap and resize to a set # of lines. Setting the numberOfLines to 0 isn't working because it cut off because my cell height is smaller than the textLabel final height. Using an arbitrary number for numberOfLines cuts off the text.
How can I resize my textLabel to fit within the frame of the cell?
Code below
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = (UITableViewCell*)[tableView #"mycell"];
cell.textLabel.text = #"LOTS OF TEXT";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
[cell.textLabel textRectForBounds:cell.bounds limitedToNumberOfLines:0];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.textLabel.numberOfLines = 0;
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.minimumScaleFactor = .5;
}
EDIT: If the cells are a set height this is straightforward. You will have to set a custom cell and textLabel in it, as I don't think you can't modify the textLabel in the basic or other preset cells the way you need to. Just add a text label and set constraints linking it to the four sides, with the minimum amount (can be zero). Labels automatically center text vertically if there is extra space, so making the label too tall shouldn't be a problem.
Original answer:
If you're not using tableView self-sizing cells, maybe to support iOS 7, then it won't matter what you do in the cell because the issue is in the tableView row heights. If a single label the classic boundingRect method will work. in tableView:heightForRowAtIndexpath: do
NSString *stringFromDataSource = #"Text For Label";
CGSize potentialSize = CGSizeMake(CGRectGetWidth(self.tableview.frame), CGFLOAT_MAX);
NSStringDrawingOptions optionsForMultiline = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading;
NSDictionary *attributes = #{
NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleBody]
}; //or whatever
CGRect rectForText = [stringFromDataSource boundingRectWithSize:potentialSize
options:optionsForMultiline
attributes:attributes
context:nil];
return CGRectGetHeight(rectForText);
Note that in your question, your various options for sizing the cell aren't going to be used if the textLabel is expanding with autolayout, unless there is a constraint capping the maximum size, at which point minimumScaleFactor and so on will kick in.
I would like to dynamically adjust the width of a UIImage inside of a UITableViewCell, I'm using the storyboard to design the UITableViewCell, I just added a label and an image, the properties get updated correctly, I'm even loading the value of the width into the label to show that it's the correct value, for the image, I'm loading a background image that I want to repeat, but the image won't update the width initially, if I scroll up and down, the images are shown as expected, here's the code for the cellForRowAtIndexPath, I've also tried to put the code on the willDisplayCell method, same result
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"mycustomcell"];
int r = [[data objectAtIndex:indexPath.row] intValue];
UIImageView *img = (UIImageView *)[cell viewWithTag:2];
img.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"some_img" ofType:#"png"]]];
CGRect frame = img.frame;
frame.size.width = r*16;
img.frame = frame;
int n = img.frame.size.width;
UILabel *label = (UILabel *)[cell viewWithTag:1];
label.text = [NSString stringWithFormat:#"custom %d", n];
[cell setNeedsDisplay];
return cell;
}
I just want this to work initially as it works after scrolling, thoughts?
The dynamic resizing of contents of a tableview cell is a well known problem. While there are kludgy workarounds, I believe proper solution depends upon whether you're using autolayout or not:
If using auto layout, make sure that your cell's image view has a width constraint, and then you can change the constraint's constant:
for (NSLayoutConstraint *constraint in img.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeWidth)
constraint.constant = r*16;
}
Frankly, I'd rather use a custom UITableViewCell subclass and have an IBOutlet for the width constraint (e.g. imageWidthConstraint), and it saves you from having to enumerate through the constraints to find the right one, and you can simply:
cell.imageWidthConstraint.constant = r*16;
If not using auto layout, you should subclass UITableViewCell, use that for your cell prototype's base class, and then override layoutSubviews, and resize the image view there. See Changing bounds of imageView of UITableViewCell.
Regardless of which approach you adopt, using a UITableViewCell subclass eliminates the need to use viewForTag construct, which makes the view controller code a little more intuitive.
argh, removing Auto Layout fixed the problem
I found a lot of similar questions, but there was no answer for me.
I'm trying to do the same cell, like note cell in iOS contacts application. There is label on left and uitextview on the right. There is only one problem when I can't set proper cell height. When delegate first asks about cell height, I call this method:
-(float)getHeight:(NSString *)descriptionFieldText
{
UITableViewCell * cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:#"Tmp"] autorelease];
cell.detailTextLabel.text = #"temp string";
[cell layoutSubviews];
float beg = cell.contentView.frame.size.width - cell.textLabel.frame.origin.x * 2 - cell.textLabel.frame.size.width;
return [m_balloon.descriptionField sizeWithFont:[cell.detailTextLabel.font fontWithSize:[cell.detailTextLabel.font pointSize]] constrainedToSize:CGSizeMake(beg, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap].height;
}
But I can't get proper height on all devices and all orientations. Please help.
I've frustrated myself with this question for a couple of days. I'm trying to add a UILabel to a UITableViewCell. I want the UILabel to span the entire width of the cell, minus 5 or 10 on both the right and left sides for looks. My problem is in programmatically determining the size of the cell's frame in which to place the label. No matter which UITableViewStyle I use, the cell.contentVew.frame.size.width value is nowhere near the width of the cell frame itself.
For example, in the table I am constructing, I can achieve my desired result by subclassing UITableViewCell and creating a UILabel with a manually determined width (through just trial and error) by:
CGRectMake(10, 12, 397, self.contentView.frame.size.height);
But it's that 397 number that's vexing me. I want a way to programmatically determine what it should be for any width table or style. This should be a simple process by just determining the width of the entire frame of the cell and then subtracting 10 or 20 so the UILabel's edges don't actually touch the edge of the cell.
However, if I set the tableViewStyle to UITableViewStyleDefault and then try:
NSLog(#"Width: %f", self.contentView.frame.size.width);
I get 320. If I set the style to any of the other three styles, the returned number is 302. Even the 320 number isn't anywhere near the width of the cell frame (as with my manually determined number of 397).
What value do I need to access that will return the entire width of the cell's drawing frame? I'm sure, as with most vexing problems, the solution will make me want to slap myself on the forehead, but I'm to the point where I'm ready for it now.
EDIT for more info:
One clarification to anyone interested. This question of mine pertains primarily to a Grouped style table. For a plain style, the answer to my question above can be determined simply in the cellForRowAtIndexPath method by:
CGFloat cellWidth = [tableView rectForRowAtIndexPath:indexPath].size.width;
The problem I'm having is that the rectForRowAtIndexPath method returns the width of the frame in which the cell is drawn, which is fine for a plain style table since the cell width is the entire width of the frame. However, in a grouped table, the width of the cell is less than the width of the frame in which it is drawn, so this method will return a number that is quite a bit wider than the width of the cell. It's possible that the width of the cell in a grouped table style is a fixed number less than the width of the table frame, so this might be the way to solve the problem. I'll investigate that and answer my own question here if that's the case.
I have determined my own answer, and I hope it helps anyone faced with the same issue. The calculation of the margin of a grouped tableView I found on this StackOverflow answer.
This code will provide a label within a tableView cell that spans the cell with a margin between the two edges of the cell, and centered vertically within the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *label;
CGFloat groupedStyleMarginWidth, tableViewWidth;
UIFont *labelFont = [UIFont boldSystemFontOfSize:17.0]; // Set to whatever you like
NSString *labelText = #"Test String";
// Calculate the margin between the cell frame and the tableView
// frame in a grouped table view style.
tableViewWidth = tableView.frame.size.width;
if (tableView.style == UITableViewStyleGrouped) {
if (tableViewWidth > 20)
groupedStyleMarginWidth = (tableViewWidth < 400) ? 10 : MAX(31, MIN(45, tableViewWidth*0.06));
else
groupedStyleMarginWidth = tableViewWidth - 10;
}
else
groupedStyleMarginWidth = 0.0;
if (cell == nil) {
CGRect tableViewRect;
CGRect labelRect;
CGFloat x, y, w, h, labelMargin;
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Retrieve the rect of the table view.
tableViewRect = [tableView rectForRowAtIndexPath:indexPath];
// Set whatever margin around the label you prefer.
labelMargin = 10;
// Determine rect values for the label.
x = tableRect.origin.x + labelMargin;
// Calculate width of label
w = tableRect.size.width - (groupedStyleMargin * 2) - (labelMargin * 2);
// Calculate height of table based on font set earlier.
h = [labelText sizeWithFont:font].height;
// Calculate y position for the label text baseline to center
// vertically within the cell.
y = (tableRect.origin.y / 2) - (h / 4);
labelRect = CGRectMake(x, y, w, h);
label = [[UILabel alloc] initWithFrame:labelRect];
label.text = labelText;
label.tag = 0;
[cell.contentView addSubview:stepLabel];
[label release];
}
else {
label = (UILabel *)[cell viewWithTag:0];
}
Sounds like this would best be handled by auto layout constraints nowadays.